aboutsummaryrefslogtreecommitdiffstats
path: root/frontends
diff options
context:
space:
mode:
Diffstat (limited to 'frontends')
-rw-r--r--frontends/ast/ast.cc105
-rw-r--r--frontends/ast/ast.h8
-rw-r--r--frontends/ast/simplify.cc2
-rw-r--r--frontends/rpc/rpc_frontend.cc2
-rw-r--r--frontends/verilog/preproc.cc620
-rw-r--r--frontends/verilog/preproc.h77
-rw-r--r--frontends/verilog/verilog_frontend.cc29
-rw-r--r--frontends/verilog/verilog_frontend.h9
-rw-r--r--frontends/verilog/verilog_lexer.l19
-rw-r--r--frontends/verilog/verilog_parser.y101
10 files changed, 730 insertions, 242 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 632a4d4f9..2c1fc25ce 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -1284,9 +1284,9 @@ AstNode * AST::find_modport(AstNode *intf, std::string name)
// Iterate over all wires in an interface and add them as wires in the AST module:
void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport)
{
- for (auto &wire_it : intfmodule->wires_){
- AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true)));
- std::string origname = log_id(wire_it.first);
+ for (auto w : intfmodule->wires()){
+ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(w->width -1, true), AstNode::mkconst_int(0, true)));
+ std::string origname = log_id(w->name);
std::string newname = intfname + "." + origname;
wire->str = newname;
if (modport != NULL) {
@@ -1320,7 +1320,7 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule
// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again
// from AST. The interface members are copied into the AST module with the prefix of the interface.
-void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces)
+void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces)
{
loadconfig();
@@ -1329,9 +1329,9 @@ void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RT
for (auto &intf : local_interfaces) {
std::string intfname = intf.first.str();
RTLIL::Module *intfmodule = intf.second;
- for (auto &wire_it : intfmodule->wires_){
- AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true)));
- std::string newname = log_id(wire_it.first);
+ for (auto w : intfmodule->wires()){
+ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(w->width -1, true), AstNode::mkconst_int(0, true)));
+ std::string newname = log_id(w->name);
newname = intfname + "." + newname;
wire->str = newname;
new_ast->children.push_back(wire);
@@ -1355,7 +1355,7 @@ void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RT
std::pair<std::string,std::string> res = split_modport_from_type(ch->str);
std::string interface_type = res.first;
std::string interface_modport = res.second; // Is "", if no modport
- if (design->modules_.count(interface_type) > 0) {
+ if (design->module(interface_type) != nullptr) {
// Add a cell to the module corresponding to the interface port such that
// it can further propagated down if needed:
AstNode *celltype_for_intf = new AstNode(AST_CELLTYPE);
@@ -1365,7 +1365,7 @@ void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RT
new_ast->children.push_back(cell_for_intf);
// Get all members of this non-overridden dummy interface instance:
- RTLIL::Module *intfmodule = design->modules_[interface_type]; // All interfaces should at this point in time (assuming
+ RTLIL::Module *intfmodule = design->module(interface_type); // All interfaces should at this point in time (assuming
// reprocess_module is called from the hierarchy pass) be
// present in design->modules_
AstModule *ast_module_of_interface = (AstModule*)intfmodule;
@@ -1408,7 +1408,7 @@ void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RT
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
// This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
-RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool /*mayfail*/)
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool /*mayfail*/)
{
AstNode *new_ast = NULL;
std::string modname = derive_common(design, parameters, &new_ast);
@@ -1460,12 +1460,19 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
// Now that the interfaces have been exploded, we can delete the dummy port related to every interface.
for(auto &intf : interfaces) {
- if(mod->wires_.count(intf.first)) {
- mod->wires_.erase(intf.first);
+ if(mod->wire(intf.first) != nullptr) {
+ // Normally, removing wires would be batched together as it's an
+ // expensive operation, however, in this case doing so would mean
+ // that a cell with the same name cannot be created (below)...
+ // Since we won't expect many interfaces to exist in a module,
+ // we can let this slide...
+ pool<RTLIL::Wire*> to_remove;
+ to_remove.insert(mod->wire(intf.first));
+ mod->remove(to_remove);
mod->fixup_ports();
- // We copy the cell of the interface to the sub-module such that it can further be found if it is propagated
- // down to sub-sub-modules etc.
- RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name);
+ // We copy the cell of the interface to the sub-module such that it
+ // can further be found if it is propagated down to sub-sub-modules etc.
+ RTLIL::Cell *new_subcell = mod->addCell(intf.first, intf.second->name);
new_subcell->set_bool_attribute("\\is_interface");
}
else {
@@ -1487,7 +1494,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
}
// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
-RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/)
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool /*mayfail*/)
{
bool quiet = lib || attributes.count(ID(blackbox)) || attributes.count(ID(whitebox));
@@ -1507,7 +1514,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
}
// create a new parametric module (when needed) and return the name of the generated module
-std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool quiet)
+std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, AstNode **new_ast_out, bool quiet)
{
std::string stripped_name = name.str();
@@ -1521,18 +1528,18 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
if (child->type != AST_PARAMETER)
continue;
para_counter++;
- std::string para_id = child->str;
- if (parameters.count(para_id) > 0) {
+ auto it = parameters.find(child->str);
+ if (it != parameters.end()) {
if (!quiet)
- log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
- para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ log("Parameter %s = %s\n", child->str.c_str(), log_signal(it->second));
+ para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second));
continue;
}
- para_id = stringf("$%d", para_counter);
- if (parameters.count(para_id) > 0) {
+ it = parameters.find(stringf("$%d", para_counter));
+ if (it != parameters.end()) {
if (!quiet)
- log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
- para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(it->second));
+ para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second));
continue;
}
}
@@ -1552,46 +1559,52 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str());
loadconfig();
+ pool<IdString> rewritten;
+ rewritten.reserve(GetSize(parameters));
+
AstNode *new_ast = ast->clone();
para_counter = 0;
for (auto child : new_ast->children) {
if (child->type != AST_PARAMETER)
continue;
para_counter++;
- std::string para_id = child->str;
- if (parameters.count(para_id) > 0) {
+ auto it = parameters.find(child->str);
+ if (it != parameters.end()) {
if (!quiet)
- log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
+ log("Parameter %s = %s\n", child->str.c_str(), log_signal(it->second));
goto rewrite_parameter;
}
- para_id = stringf("$%d", para_counter);
- if (parameters.count(para_id) > 0) {
+ it = parameters.find(stringf("$%d", para_counter));
+ if (it != parameters.end()) {
if (!quiet)
- log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(it->second));
goto rewrite_parameter;
}
continue;
rewrite_parameter:
delete child->children.at(0);
- if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) {
+ if ((it->second.flags & RTLIL::CONST_FLAG_REAL) != 0) {
child->children[0] = new AstNode(AST_REALVALUE);
- child->children[0]->realvalue = std::stod(parameters[para_id].decode_string());
- } else if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0)
- child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string());
+ child->children[0]->realvalue = std::stod(it->second.decode_string());
+ } else if ((it->second.flags & RTLIL::CONST_FLAG_STRING) != 0)
+ child->children[0] = AstNode::mkconst_str(it->second.decode_string());
else
- child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
- parameters.erase(para_id);
+ child->children[0] = AstNode::mkconst_bits(it->second.bits, (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0);
+ rewritten.insert(it->first);
}
- for (auto param : parameters) {
- AstNode *defparam = new AstNode(AST_DEFPARAM, new AstNode(AST_IDENTIFIER));
- defparam->children[0]->str = param.first.str();
- if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
- defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string()));
- else
- defparam->children.push_back(AstNode::mkconst_bits(param.second.bits, (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0));
- new_ast->children.push_back(defparam);
- }
+ if (GetSize(rewritten) < GetSize(parameters))
+ for (const auto &param : parameters) {
+ if (rewritten.count(param.first))
+ continue;
+ AstNode *defparam = new AstNode(AST_DEFPARAM, new AstNode(AST_IDENTIFIER));
+ defparam->children[0]->str = param.first.str();
+ if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
+ defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string()));
+ else
+ defparam->children.push_back(AstNode::mkconst_bits(param.second.bits, (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0));
+ new_ast->children.push_back(defparam);
+ }
(*new_ast_out) = new_ast;
return modname;
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index e27ab10c2..3dd40238f 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -312,10 +312,10 @@ namespace AST
AstNode *ast;
bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, pwires, autowire;
~AstModule() YS_OVERRIDE;
- RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
- RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE;
- std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool quiet = false);
- void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE;
+ RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool mayfail) YS_OVERRIDE;
+ RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail) YS_OVERRIDE;
+ std::string derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, AstNode **new_ast_out, bool quiet = false);
+ void reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) YS_OVERRIDE;
RTLIL::Module *clone() const YS_OVERRIDE;
void loadconfig() const;
};
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 10e9f2683..f801a17e0 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1727,7 +1727,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
did_something = true;
newNode = new AstNode(AST_CASE, shift_expr);
- for (int i = 0; i <= source_width-result_width; i++) {
+ for (int i = 0; i < source_width; i++) {
int start_bit = children[0]->id2ast->range_right + i;
AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true));
AstNode *lvalue = children[0]->clone();
diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc
index add17c243..fb3db70d2 100644
--- a/frontends/rpc/rpc_frontend.cc
+++ b/frontends/rpc/rpc_frontend.cc
@@ -157,7 +157,7 @@ struct RpcServer {
struct RpcModule : RTLIL::Module {
std::shared_ptr<RpcServer> server;
- RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/) YS_OVERRIDE {
+ RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool /*mayfail*/) YS_OVERRIDE {
std::string stripped_name = name.str();
if (stripped_name.compare(0, 9, "$abstract") == 0)
stripped_name = stripped_name.substr(9);
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 f2c1c227f..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>
@@ -51,7 +52,6 @@ static void add_package_types(std::map<std::string, AST::AstNode *> &user_types,
{
// prime the parser's user type lookup table with the package qualified names
// of typedefed names in the packages seen so far.
- user_types.clear();
for (const auto &pkg : package_list) {
log_assert(pkg->type==AST::AST_PACKAGE);
for (const auto &node: pkg->children) {
@@ -61,6 +61,8 @@ static void add_package_types(std::map<std::string, AST::AstNode *> &user_types,
}
}
}
+ user_type_stack.clear();
+ user_type_stack.push_back(new UserTypeMap());
}
struct VerilogFrontend : public Frontend {
@@ -253,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;
@@ -369,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") {
@@ -421,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) {
@@ -430,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()) {
@@ -460,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);
@@ -592,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) {
@@ -601,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 73ea51e6c..444cc7297 100644
--- a/frontends/verilog/verilog_frontend.h
+++ b/frontends/verilog/verilog_frontend.h
@@ -45,8 +45,9 @@ 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
- extern std::map<std::string, AST::AstNode*> user_types;
+ // 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;
@@ -85,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 74e8dce7f..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
@@ -376,9 +388,9 @@ supply1 { return TOK_SUPPLY1; }
// package qualifier
auto s = std::string("\\") + yytext;
if (pkg_user_types.count(s) > 0) {
- // found it
+ // package qualified typedefed name
yylval->string = new std::string(s);
- return TOK_USER_TYPE;
+ return TOK_PKG_USER_TYPE;
}
else {
// backup before :: just return first part
@@ -391,7 +403,8 @@ supply1 { return TOK_SUPPLY1; }
[a-zA-Z_$][a-zA-Z0-9_$]* {
auto s = std::string("\\") + yytext;
- if (user_types.count(s) > 0) {
+ if (isUserType(s)) {
+ // previously typedefed name
yylval->string = new std::string(s);
return TOK_USER_TYPE;
}
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index f7e3afd13..3f28f828d 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -54,7 +54,7 @@ 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::map<std::string, AstNode*> user_types;
+ std::vector<UserTypeMap*> user_type_stack;
std::map<std::string, AstNode*> pkg_user_types;
std::vector<AstNode*> ast_stack;
struct AstNode *astbuf1, *astbuf2, *astbuf3;
@@ -130,14 +130,10 @@ struct specify_rise_fall {
static void addTypedefNode(std::string *name, AstNode *node)
{
log_assert(node);
- // seems to be support for local scoped typedefs in simplify()
- // and tests redefine types.
- //if (user_types.count(*name) > 0) {
- // frontend_verilog_yyerror("Type already defined.");
- //}
auto *tnode = new AstNode(AST_TYPEDEF, node);
tnode->str = *name;
- user_types[*name] = tnode;
+ auto user_types = user_type_stack.back();
+ (*user_types)[*name] = tnode;
if (current_ast_mod && current_ast_mod->type == AST_PACKAGE) {
// typedef inside a package so we need the qualified name
auto qname = current_ast_mod->str + "::" + (*name).substr(1);
@@ -147,6 +143,24 @@ static void addTypedefNode(std::string *name, AstNode *node)
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);
@@ -189,7 +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
+%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
@@ -355,11 +369,14 @@ hierarchical_id:
hierarchical_type_id:
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);
@@ -367,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'.",
@@ -378,7 +395,7 @@ module:
ast_stack.pop_back();
log_assert(ast_stack.size() == 1);
current_ast_mod = NULL;
- user_types.clear();
+ exitTypeScope();
};
module_para_opt:
@@ -482,17 +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;
- user_types.clear();
+ exitTypeScope();
};
package_body:
@@ -505,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);
@@ -513,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'.",
@@ -522,7 +543,7 @@ interface:
ast_stack.pop_back();
log_assert(ast_stack.size() == 1);
current_ast_mod = NULL;
- user_types.clear();
+ exitTypeScope();
};
interface_body:
@@ -1621,7 +1642,7 @@ assign_expr:
};
type_name: TOK_ID // first time seen
- | TOK_USER_TYPE // redefinition
+ | TOK_USER_TYPE { if (isInLocalScope($1)) frontend_verilog_yyerror("Duplicate declaration of TYPEDEF '%s'", $1->c_str()+1); }
;
typedef_decl:
@@ -2210,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 '(' {
@@ -2301,6 +2323,8 @@ behavioral_stmt:
ast_stack.pop_back();
};
+ ;
+
unique_case_attr:
/* empty */ {
$$ = false;
@@ -2498,6 +2522,7 @@ gen_stmt:
} simple_behavioral_stmt ';' expr {
ast_stack.back()->children.push_back($6);
} ';' simple_behavioral_stmt ')' gen_stmt_block {
+ SET_AST_NODE_LOC(ast_stack.back(), @1, @11);
ast_stack.pop_back();
} |
TOK_IF '(' expr ')' {
@@ -2506,6 +2531,7 @@ gen_stmt:
ast_stack.push_back(node);
ast_stack.back()->children.push_back($3);
} gen_stmt_block opt_gen_else {
+ SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
ast_stack.pop_back();
} |
case_type '(' expr ')' {
@@ -2514,18 +2540,21 @@ gen_stmt:
ast_stack.push_back(node);
} gen_case_body TOK_ENDCASE {
case_type_stack.pop_back();
+ SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
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;
+ SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
ast_stack.pop_back();
} |
TOK_MSG_TASKS {
@@ -2535,6 +2564,7 @@ gen_stmt:
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
} opt_arg_list ';'{
+ SET_AST_NODE_LOC(ast_stack.back(), @1, @3);
ast_stack.pop_back();
};
@@ -2544,6 +2574,7 @@ gen_stmt_block:
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
} gen_stmt_or_module_body_stmt {
+ SET_AST_NODE_LOC(ast_stack.back(), @2, @2);
ast_stack.pop_back();
};