aboutsummaryrefslogtreecommitdiffstats
path: root/frontends
diff options
context:
space:
mode:
authorClaire Xen <claire@clairexen.net>2022-02-11 16:03:12 +0100
committerGitHub <noreply@github.com>2022-02-11 16:03:12 +0100
commit49545c73f7f5a5cf73d287fd371f2ff39311f621 (patch)
treed0f20b8def36e551c6735d4fc6033aaa2633fe80 /frontends
parent90b40aa51f7d666792d4f0b1830ee75b81678a1f (diff)
parente0165188669fcef2c5784c9916683889a2164e5d (diff)
downloadyosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.tar.gz
yosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.tar.bz2
yosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.zip
Merge branch 'master' into clk2ff-better-names
Diffstat (limited to 'frontends')
-rw-r--r--frontends/aiger/aigerparse.cc2
-rw-r--r--frontends/aiger/aigerparse.h2
-rw-r--r--frontends/ast/Makefile.inc1
-rw-r--r--frontends/ast/ast.cc311
-rw-r--r--frontends/ast/ast.h50
-rw-r--r--frontends/ast/ast_binding.cc49
-rw-r--r--frontends/ast/ast_binding.h58
-rw-r--r--frontends/ast/dpicall.cc2
-rw-r--r--frontends/ast/genrtlil.cc268
-rw-r--r--frontends/ast/simplify.cc688
-rw-r--r--frontends/blif/blifparse.cc39
-rw-r--r--frontends/blif/blifparse.h2
-rw-r--r--frontends/json/jsonparse.cc52
-rw-r--r--frontends/liberty/liberty.cc2
-rw-r--r--frontends/rtlil/rtlil_frontend.cc2
-rw-r--r--frontends/rtlil/rtlil_frontend.h2
-rw-r--r--frontends/rtlil/rtlil_lexer.l3
-rw-r--r--frontends/rtlil/rtlil_parser.y27
-rw-r--r--frontends/verific/Makefile.inc2
-rw-r--r--frontends/verific/README8
-rw-r--r--frontends/verific/verific.cc521
-rw-r--r--frontends/verific/verific.h3
-rw-r--r--frontends/verific/verificsva.cc2
-rw-r--r--frontends/verilog/const2ast.cc2
-rw-r--r--frontends/verilog/preproc.cc66
-rw-r--r--frontends/verilog/preproc.h3
-rw-r--r--frontends/verilog/verilog_frontend.cc21
-rw-r--r--frontends/verilog/verilog_frontend.h4
-rw-r--r--frontends/verilog/verilog_lexer.l52
-rw-r--r--frontends/verilog/verilog_parser.y499
30 files changed, 2300 insertions, 443 deletions
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index 463c5965b..cb19b8413 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h
index 251a24977..81b955947 100644
--- a/frontends/aiger/aigerparse.h
+++ b/frontends/aiger/aigerparse.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc
index 91d917c91..9e6eee1e8 100644
--- a/frontends/ast/Makefile.inc
+++ b/frontends/ast/Makefile.inc
@@ -3,4 +3,5 @@ OBJS += frontends/ast/ast.o
OBJS += frontends/ast/simplify.o
OBJS += frontends/ast/genrtlil.o
OBJS += frontends/ast/dpicall.o
+OBJS += frontends/ast/ast_binding.o
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 57552d86c..7be8ab565 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -52,8 +52,10 @@ namespace AST_INTERNAL {
const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL;
RTLIL::SigSpec ignoreThisSignalsInInitial;
AstNode *current_always, *current_top_block, *current_block, *current_block_child;
- AstModule *current_module;
+ Module *current_module;
bool current_always_clocked;
+ dict<std::string, int> current_memwr_count;
+ dict<std::string, pool<int>> current_memwr_visible;
}
// convert node types to string
@@ -175,6 +177,7 @@ std::string AST::type2str(AstNodeType type)
X(AST_STRUCT)
X(AST_UNION)
X(AST_STRUCT_ITEM)
+ X(AST_BIND)
#undef X
default:
log_abort();
@@ -196,7 +199,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id)
// create new node (AstNode constructor)
// (the optional child arguments make it easier to create AST trees)
-AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3)
+AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3, AstNode *child4)
{
static unsigned int hashidx_count = 123456789;
hashidx_count = mkhash_xorshift(hashidx_count);
@@ -233,6 +236,8 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
children.push_back(child2);
if (child3)
children.push_back(child3);
+ if (child4)
+ children.push_back(child4);
}
// create a (deep recursive) copy of a node
@@ -317,6 +322,8 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
fprintf(f, " reg");
if (is_signed)
fprintf(f, " signed");
+ if (is_unsized)
+ fprintf(f, " unsized");
if (basic_prep)
fprintf(f, " basic_prep");
if (lookahead)
@@ -847,7 +854,7 @@ RTLIL::Const AstNode::bitsAsConst(int width)
return bitsAsConst(width, is_signed);
}
-RTLIL::Const AstNode::asAttrConst()
+RTLIL::Const AstNode::asAttrConst() const
{
log_assert(type == AST_CONSTANT);
@@ -862,8 +869,17 @@ RTLIL::Const AstNode::asAttrConst()
return val;
}
-RTLIL::Const AstNode::asParaConst()
+RTLIL::Const AstNode::asParaConst() const
{
+ if (type == AST_REALVALUE)
+ {
+ AstNode *strnode = AstNode::mkconst_str(stringf("%f", realvalue));
+ RTLIL::Const val = strnode->asAttrConst();
+ val.flags |= RTLIL::CONST_FLAG_REAL;
+ delete strnode;
+ return val;
+ }
+
RTLIL::Const val = asAttrConst();
if (is_signed)
val.flags |= RTLIL::CONST_FLAG_SIGNED;
@@ -968,8 +984,15 @@ void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast)
obj->attributes[ID::src] = ast->loc_string();
}
-// create a new AstModule from an AST_MODULE AST node
-static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false)
+static bool param_has_no_default(const AstNode *param) {
+ const auto &children = param->children;
+ log_assert(param->type == AST_PARAMETER);
+ log_assert(children.size() <= 2);
+ return children.empty() ||
+ (children.size() == 1 && children[0]->type == AST_RANGE);
+}
+
+static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false)
{
log_assert(current_scope.empty());
log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE);
@@ -980,11 +1003,13 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str());
}
- current_module = new AstModule;
- current_module->ast = NULL;
- current_module->name = ast->str;
- set_src_attr(current_module, ast);
- current_module->set_bool_attribute(ID::cells_not_processed);
+ AstModule *module = new AstModule;
+ current_module = module;
+
+ module->ast = NULL;
+ module->name = ast->str;
+ set_src_attr(module, ast);
+ module->set_bool_attribute(ID::cells_not_processed);
current_ast_mod = ast;
AstNode *ast_before_simplify;
@@ -1006,6 +1031,10 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
if (!defer)
{
+ for (const AstNode *node : ast->children)
+ if (node->type == AST_PARAMETER && param_has_no_default(node))
+ log_file_error(node->filename, node->location.first_line, "Parameter `%s' has no default value and has not been overridden!\n", node->str.c_str());
+
bool blackbox_module = flag_lib;
if (!blackbox_module && !flag_noblackbox) {
@@ -1023,7 +1052,11 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
}
}
+ // simplify this module or interface using the current design as context
+ // for lookup up ports and wires within cells
+ set_simplify_design_context(design);
while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { }
+ set_simplify_design_context(nullptr);
if (flag_dump_ast2) {
log("Dumping AST after simplification:\n");
@@ -1120,7 +1153,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
- current_module->attributes[attr.first] = attr.second->asAttrConst();
+ module->attributes[attr.first] = attr.second->asAttrConst();
}
for (size_t i = 0; i < ast->children.size(); i++) {
AstNode *node = ast->children[i];
@@ -1148,35 +1181,93 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
continue;
- current_module->attributes[attr.first] = attr.second->asAttrConst();
+ module->attributes[attr.first] = attr.second->asAttrConst();
}
+ for (const AstNode *node : ast->children)
+ if (node->type == AST_PARAMETER)
+ current_module->avail_parameters(node->str);
}
if (ast->type == AST_INTERFACE)
- current_module->set_bool_attribute(ID::is_interface);
- current_module->ast = ast_before_simplify;
- current_module->nolatches = flag_nolatches;
- current_module->nomeminit = flag_nomeminit;
- current_module->nomem2reg = flag_nomem2reg;
- current_module->mem2reg = flag_mem2reg;
- current_module->noblackbox = flag_noblackbox;
- current_module->lib = flag_lib;
- current_module->nowb = flag_nowb;
- current_module->noopt = flag_noopt;
- current_module->icells = flag_icells;
- current_module->pwires = flag_pwires;
- current_module->autowire = flag_autowire;
- current_module->fixup_ports();
+ module->set_bool_attribute(ID::is_interface);
+ module->ast = ast_before_simplify;
+ module->nolatches = flag_nolatches;
+ module->nomeminit = flag_nomeminit;
+ module->nomem2reg = flag_nomem2reg;
+ module->mem2reg = flag_mem2reg;
+ module->noblackbox = flag_noblackbox;
+ module->lib = flag_lib;
+ module->nowb = flag_nowb;
+ module->noopt = flag_noopt;
+ module->icells = flag_icells;
+ module->pwires = flag_pwires;
+ module->autowire = flag_autowire;
+ module->fixup_ports();
if (flag_dump_rtlil) {
log("Dumping generated RTLIL:\n");
- log_module(current_module);
+ log_module(module);
log("--- END OF RTLIL DUMP ---\n");
}
+ design->add(current_module);
return current_module;
}
+RTLIL::Module *
+AST_INTERNAL::process_and_replace_module(RTLIL::Design *design,
+ RTLIL::Module *old_module,
+ AstNode *new_ast,
+ AstNode *original_ast)
+{
+ // The old module will be deleted. Rename and mark for deletion, using
+ // a static counter to make sure we get a unique name.
+ static unsigned counter;
+ std::ostringstream new_name;
+ new_name << old_module->name.str()
+ << "_before_process_and_replace_module_"
+ << counter;
+ ++counter;
+
+ design->rename(old_module, new_name.str());
+ old_module->set_bool_attribute(ID::to_delete);
+
+ // Check if the module was the top module. If it was, we need to remove
+ // the top attribute and put it on the new module.
+ bool is_top = false;
+ if (old_module->get_bool_attribute(ID::initial_top)) {
+ old_module->attributes.erase(ID::initial_top);
+ is_top = true;
+ }
+
+ // Generate RTLIL from AST for the new module and add to the design:
+ RTLIL::Module* new_module = process_module(design, new_ast, false, original_ast);
+
+ if (is_top)
+ new_module->set_bool_attribute(ID::top);
+
+ return new_module;
+}
+
+// renames identifiers in tasks and functions within a package
+static void rename_in_package_stmts(AstNode *pkg)
+{
+ std::unordered_set<std::string> idents;
+ for (AstNode *item : pkg->children)
+ idents.insert(item->str);
+ std::function<void(AstNode*)> rename =
+ [&rename, &idents, pkg](AstNode *node) {
+ for (AstNode *child : node->children) {
+ if (idents.count(child->str))
+ child->str = pkg->str + "::" + child->str.substr(1);
+ rename(child);
+ }
+ };
+ for (AstNode *item : pkg->children)
+ if (item->type == AST_FUNCTION || item->type == AST_TASK)
+ rename(item);
+}
+
// create AstModule instances for all modules in the AST tree and add them to 'design'
void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire)
@@ -1202,12 +1293,12 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
flag_autowire = autowire;
log_assert(current_ast->type == AST_DESIGN);
- for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++)
+ for (AstNode *child : current_ast->children)
{
- if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE)
+ if (child->type == AST_MODULE || child->type == AST_INTERFACE)
{
for (auto n : design->verilog_globals)
- (*it)->children.push_back(n->clone());
+ child->children.push_back(n->clone());
// append nodes from previous packages using package-qualified names
for (auto &n : design->verilog_packages) {
@@ -1222,45 +1313,63 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
} else {
cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1);
}
- (*it)->children.push_back(cloned_node);
+ child->children.push_back(cloned_node);
}
}
- if (flag_icells && (*it)->str.compare(0, 2, "\\$") == 0)
- (*it)->str = (*it)->str.substr(1);
+ if (flag_icells && child->str.compare(0, 2, "\\$") == 0)
+ child->str = child->str.substr(1);
- if (defer)
- (*it)->str = "$abstract" + (*it)->str;
+ bool defer_local = defer;
+ if (!defer_local)
+ for (const AstNode *node : child->children)
+ if (node->type == AST_PARAMETER && param_has_no_default(node))
+ {
+ log("Deferring `%s' because it contains parameter(s) without defaults.\n", child->str.c_str());
+ defer_local = true;
+ break;
+ }
- if (design->has((*it)->str)) {
- RTLIL::Module *existing_mod = design->module((*it)->str);
+
+ if (defer_local)
+ child->str = "$abstract" + child->str;
+
+ if (design->has(child->str)) {
+ RTLIL::Module *existing_mod = design->module(child->str);
if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) {
- log_file_error((*it)->filename, (*it)->location.first_line, "Re-definition of module `%s'!\n", (*it)->str.c_str());
+ log_file_error(child->filename, child->location.first_line, "Re-definition of module `%s'!\n", child->str.c_str());
} else if (nooverwrite) {
log("Ignoring re-definition of module `%s' at %s.\n",
- (*it)->str.c_str(), (*it)->loc_string().c_str());
+ child->str.c_str(), child->loc_string().c_str());
continue;
} else {
log("Replacing existing%s module `%s' at %s.\n",
existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "",
- (*it)->str.c_str(), (*it)->loc_string().c_str());
+ child->str.c_str(), child->loc_string().c_str());
design->remove(existing_mod);
}
}
- design->add(process_module(*it, defer));
+ process_module(design, child, defer_local);
current_ast_mod = nullptr;
}
- else if ((*it)->type == AST_PACKAGE) {
+ else if (child->type == AST_PACKAGE) {
// process enum/other declarations
- (*it)->simplify(true, false, false, 1, -1, false, false);
- design->verilog_packages.push_back((*it)->clone());
+ child->simplify(true, false, false, 1, -1, false, false);
+ rename_in_package_stmts(child);
+ design->verilog_packages.push_back(child->clone());
current_scope.clear();
}
+ else if (child->type == AST_BIND) {
+ // top-level bind construct
+ for (RTLIL::Binding *binding : child->genBindings())
+ design->add(binding);
+ }
else {
// must be global definition
- (*it)->simplify(false, false, false, 1, -1, false, false); //process enum/other declarations
- design->verilog_globals.push_back((*it)->clone());
+ if (child->type == AST_PARAMETER)
+ child->type = AST_LOCALPARAM; // cannot be overridden
+ design->verilog_globals.push_back(child->clone());
current_scope.clear();
}
}
@@ -1351,13 +1460,32 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule
}
}
+// AstModules may contain cells marked with ID::reprocess_after, which indicates
+// that it should be reprocessed once the specified module has been elaborated.
+bool AstModule::reprocess_if_necessary(RTLIL::Design *design)
+{
+ for (const RTLIL::Cell *cell : cells())
+ {
+ std::string modname = cell->get_string_attribute(ID::reprocess_after);
+ if (modname.empty())
+ continue;
+ if (design->module(modname) || design->module("$abstract" + modname)) {
+ log("Reprocessing module %s because instantiated module %s has become available.\n",
+ log_id(name), log_id(modname));
+ loadconfig();
+ process_and_replace_module(design, this, ast, NULL);
+ return true;
+ }
+ }
+ return false;
+}
+
// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again
// from AST. The interface members are copied into the AST module with the prefix of the interface.
-void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces)
+void AstModule::expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces)
{
loadconfig();
- bool is_top = false;
AstNode *new_ast = ast->clone();
for (auto &intf : local_interfaces) {
std::string intfname = intf.first.str();
@@ -1414,29 +1542,15 @@ void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdStri
}
}
- // The old module will be deleted. Rename and mark for deletion:
- std::string original_name = this->name.str();
- std::string changed_name = original_name + "_before_replacing_local_interfaces";
- design->rename(this, changed_name);
- this->set_bool_attribute(ID::to_delete);
+ // Generate RTLIL from AST for the new module and add to the design,
+ // renaming this module to move it out of the way.
+ RTLIL::Module* new_module =
+ process_and_replace_module(design, this, new_ast, ast_before_replacing_interface_ports);
- // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the
- // new module.
- if (this->get_bool_attribute(ID::initial_top)) {
- this->attributes.erase(ID::initial_top);
- is_top = true;
- }
-
- // Generate RTLIL from AST for the new module and add to the design:
- AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports);
- delete(new_ast);
- design->add(newmod);
- RTLIL::Module* mod = design->module(original_name);
- if (is_top)
- mod->set_bool_attribute(ID::top);
+ delete new_ast;
// Set the attribute "interfaces_replaced_in_module" so that it does not happen again.
- mod->set_bool_attribute(ID::interfaces_replaced_in_module);
+ new_module->set_bool_attribute(ID::interfaces_replaced_in_module);
}
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
@@ -1486,7 +1600,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr
explode_interface_port(new_ast, intfmodule, intfname, modport);
}
- design->add(process_module(new_ast, false));
+ process_module(design, new_ast, false);
design->module(modname)->check();
RTLIL::Module* mod = design->module(modname);
@@ -1537,7 +1651,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr
if (!design->has(modname)) {
new_ast->str = modname;
- design->add(process_module(new_ast, false, NULL, quiet));
+ process_module(design, new_ast, false, NULL, quiet);
design->module(modname)->check();
} else if (!quiet) {
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
@@ -1547,6 +1661,40 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr
return modname;
}
+static std::string serialize_param_value(const RTLIL::Const &val) {
+ std::string res;
+ if (val.flags & RTLIL::ConstFlags::CONST_FLAG_STRING)
+ res.push_back('t');
+ if (val.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED)
+ res.push_back('s');
+ if (val.flags & RTLIL::ConstFlags::CONST_FLAG_REAL)
+ res.push_back('r');
+ res += stringf("%d", GetSize(val));
+ res.push_back('\'');
+ for (int i = GetSize(val) - 1; i >= 0; i--) {
+ switch (val.bits[i]) {
+ case RTLIL::State::S0: res.push_back('0'); break;
+ case RTLIL::State::S1: res.push_back('1'); break;
+ case RTLIL::State::Sx: res.push_back('x'); break;
+ case RTLIL::State::Sz: res.push_back('z'); break;
+ case RTLIL::State::Sa: res.push_back('?'); break;
+ case RTLIL::State::Sm: res.push_back('m'); break;
+ }
+ }
+ return res;
+}
+
+std::string AST::derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters) {
+ std::string para_info;
+ for (const auto &elem : parameters)
+ para_info += stringf("%s=%s", elem.first.c_str(), serialize_param_value(elem.second).c_str());
+
+ if (para_info.size() > 60)
+ return "$paramod$" + sha1(para_info) + stripped_name;
+ else
+ return "$paramod" + stripped_name + para_info;
+}
+
// create a new parametric module (when needed) and return the name of the generated module
std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, AstNode **new_ast_out, bool quiet)
{
@@ -1555,9 +1703,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
if (stripped_name.compare(0, 9, "$abstract") == 0)
stripped_name = stripped_name.substr(9);
- std::string para_info;
-
int para_counter = 0;
+ std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters;
for (const auto child : ast->children) {
if (child->type != AST_PARAMETER)
continue;
@@ -1566,25 +1713,21 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
if (it != parameters.end()) {
if (!quiet)
log("Parameter %s = %s\n", child->str.c_str(), log_signal(it->second));
- para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second));
+ named_parameters.emplace_back(child->str, it->second);
continue;
}
it = parameters.find(stringf("$%d", para_counter));
if (it != parameters.end()) {
if (!quiet)
log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(it->second));
- para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second));
+ named_parameters.emplace_back(child->str, it->second);
continue;
}
}
- std::string modname;
- if (parameters.size() == 0)
- modname = stripped_name;
- else if (para_info.size() > 60)
- modname = "$paramod$" + sha1(para_info) + stripped_name;
- else
- modname = "$paramod" + stripped_name + para_info;
+ std::string modname = stripped_name;
+ if (parameters.size()) // not named_parameters to cover hierarchical defparams
+ modname = derived_module_name(stripped_name, named_parameters);
if (design->has(modname))
return modname;
@@ -1619,6 +1762,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
}
continue;
rewrite_parameter:
+ if (param_has_no_default(child))
+ child->children.insert(child->children.begin(), nullptr);
delete child->children.at(0);
if ((it->second.flags & RTLIL::CONST_FLAG_REAL) != 0) {
child->children[0] = new AstNode(AST_REALVALUE);
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 1c9a6ee47..48ec9a063 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -1,7 +1,7 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -160,7 +160,8 @@ namespace AST
AST_TYPEDEF,
AST_STRUCT,
AST_UNION,
- AST_STRUCT_ITEM
+ AST_STRUCT_ITEM,
+ AST_BIND
};
struct AstSrcLocType {
@@ -220,7 +221,7 @@ namespace AST
AstSrcLocType location;
// creating and deleting nodes
- AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL);
+ AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr);
AstNode *clone() const;
void cloneInto(AstNode *other) const;
void delete_children();
@@ -261,6 +262,7 @@ namespace AST
void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes);
void meminfo(int &mem_width, int &mem_size, int &addr_bits);
bool detect_latch(const std::string &var);
+ const RTLIL::Module* lookup_cell_module();
// additional functionality for evaluating constant functions
struct varinfo_t {
@@ -283,6 +285,9 @@ namespace AST
void dumpAst(FILE *f, std::string indent) const;
void dumpVlog(FILE *f, std::string indent) const;
+ // Generate RTLIL for a bind construct
+ std::vector<RTLIL::Binding *> genBindings() const;
+
// used by genRTLIL() for detecting expression width and sign
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL);
@@ -291,7 +296,7 @@ namespace AST
// for expressions the resulting signal vector is returned
// all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module
RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false);
- RTLIL::SigSpec genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL);
+ RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL);
// compare AST nodes
bool operator==(const AstNode &other) const;
@@ -309,8 +314,8 @@ namespace AST
RTLIL::Const bitsAsConst(int width, bool is_signed);
RTLIL::Const bitsAsConst(int width = -1);
RTLIL::Const bitsAsUnsizedConst(int width);
- RTLIL::Const asAttrConst();
- RTLIL::Const asParaConst();
+ RTLIL::Const asAttrConst() const;
+ RTLIL::Const asParaConst() const;
uint64_t asInt(bool is_signed);
bool bits_only_01() const;
bool asBool() const;
@@ -326,6 +331,9 @@ namespace AST
// helpers for locations
std::string loc_string() const;
+
+ // Helper for looking up identifiers which are prefixed with the current module name
+ std::string try_pop_module_prefix() const;
};
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
@@ -341,7 +349,8 @@ namespace AST
RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool mayfail) 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) 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) override;
+ void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override;
+ bool reprocess_if_necessary(RTLIL::Design *design) override;
RTLIL::Module *clone() const override;
void loadconfig() const;
};
@@ -367,6 +376,17 @@ namespace AST
// Helper for setting the src attribute.
void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);
+
+ // struct helper exposed from simplify for genrtlil
+ AstNode *make_struct_member_range(AstNode *node, AstNode *member_node);
+
+ // generate standard $paramod... derived module name; parameters should be
+ // in the order they are declared in the instantiated module
+ std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters);
+
+ // used to provide simplify() access to the current design for looking up
+ // modules, ports, wires, etc.
+ void set_simplify_design_context(const RTLIL::Design *design);
}
namespace AST_INTERNAL
@@ -379,10 +399,24 @@ namespace AST_INTERNAL
extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr;
extern RTLIL::SigSpec ignoreThisSignalsInInitial;
extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child;
- extern AST::AstModule *current_module;
+ extern RTLIL::Module *current_module;
extern bool current_always_clocked;
+ extern dict<std::string, int> current_memwr_count;
+ extern dict<std::string, pool<int>> current_memwr_visible;
struct LookaheadRewriter;
struct ProcessGenerator;
+
+ // Create and add a new AstModule from new_ast, then use it to replace
+ // old_module in design, renaming old_module to move it out of the way.
+ // Return the new module.
+ //
+ // If original_ast is not null, it will be used as the AST node for the
+ // new module. Otherwise, new_ast will be used.
+ RTLIL::Module *
+ process_and_replace_module(RTLIL::Design *design,
+ RTLIL::Module *old_module,
+ AST::AstNode *new_ast,
+ AST::AstNode *original_ast = nullptr);
}
YOSYS_NAMESPACE_END
diff --git a/frontends/ast/ast_binding.cc b/frontends/ast/ast_binding.cc
new file mode 100644
index 000000000..c20d1df4d
--- /dev/null
+++ b/frontends/ast/ast_binding.cc
@@ -0,0 +1,49 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ast_binding.h"
+#include "ast.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+using namespace AST_INTERNAL;
+
+AST::Binding::Binding(RTLIL::IdString target_type,
+ RTLIL::IdString target_name,
+ const AstNode &cell)
+ : RTLIL::Binding(target_type, target_name),
+ ast_node(cell.clone())
+{
+ log_assert(cell.type == AST_CELL);
+}
+
+std::string
+AST::Binding::describe() const
+{
+ std::ostringstream oss;
+ oss << "directive to bind " << ast_node->str
+ << " to " << target_name.str();
+ if (!target_type.empty())
+ oss << " (target type: "
+ << target_type.str()
+ << ")";
+ return oss.str();
+}
+
+PRIVATE_NAMESPACE_END
diff --git a/frontends/ast/ast_binding.h b/frontends/ast/ast_binding.h
new file mode 100644
index 000000000..641497d52
--- /dev/null
+++ b/frontends/ast/ast_binding.h
@@ -0,0 +1,58 @@
+/* -*- c++ -*-
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * ---
+ *
+ * This header declares the AST::Binding class
+ *
+ * This is used to support the bind directive and is to RTLIL::Binding as
+ * AST::AstModule is to RTLIL::Module, holding a syntax-level representation of
+ * cells until we get to a stage where they make sense. In the case of a bind
+ * directive, this is when we elaborate the design in the hierarchy pass.
+ *
+ */
+
+#ifndef AST_BINDING_H
+#define AST_BINDING_H
+
+#include "kernel/rtlil.h"
+#include "kernel/binding.h"
+
+#include <memory>
+
+YOSYS_NAMESPACE_BEGIN
+
+namespace AST
+{
+ class Binding : public RTLIL::Binding
+ {
+ public:
+ Binding(RTLIL::IdString target_type,
+ RTLIL::IdString target_name,
+ const AstNode &cell);
+
+ std::string describe() const override;
+
+ private:
+ // The syntax-level representation of the cell to be bound.
+ std::unique_ptr<AstNode> ast_node;
+ };
+}
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc
index 948c9083c..12a7e1183 100644
--- a/frontends/ast/dpicall.cc
+++ b/frontends/ast/dpicall.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index d4299bf69..4c25287ad 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -28,8 +28,10 @@
#include "kernel/log.h"
#include "kernel/utils.h"
+#include "kernel/binding.h"
#include "libs/sha1/sha1.h"
#include "ast.h"
+#include "ast_binding.h"
#include <sstream>
#include <stdarg.h>
@@ -319,16 +321,14 @@ struct AST_INTERNAL::ProcessGenerator
LookaheadRewriter la_rewriter(always);
// generate process and simple root case
- proc = new RTLIL::Process;
+ proc = current_module->addProcess(stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++));
set_src_attr(proc, always);
- proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++);
for (auto &attr : always->attributes) {
if (attr.second->type != AST_CONSTANT)
log_file_error(always->filename, always->location.first_line, "Attribute `%s' with non-constant value!\n",
attr.first.c_str());
proc->attributes[attr.first] = attr.second->asAttrConst();
}
- current_module->processes[proc->name] = proc;
current_case = &proc->root_case;
// create initial temporary signal for all output registers
@@ -399,6 +399,9 @@ struct AST_INTERNAL::ProcessGenerator
if (child->type == AST_BLOCK)
processAst(child);
+ for (auto sync: proc->syncs)
+ processMemWrites(sync);
+
if (initSyncSignals.size() > 0)
{
RTLIL::SyncRule *sync = new RTLIL::SyncRule;
@@ -563,7 +566,7 @@ struct AST_INTERNAL::ProcessGenerator
case AST_ASSIGN_LE:
{
RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue;
- RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap());
+ RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), true, &subst_rvalue_map.stdmap());
pool<SigBit> lvalue_sigbits;
for (int i = 0; i < GetSize(lvalue); i++) {
@@ -590,9 +593,13 @@ struct AST_INTERNAL::ProcessGenerator
case AST_CASE:
{
+ int width_hint;
+ bool sign_hint;
+ ast->detectSignWidth(width_hint, sign_hint);
+
RTLIL::SwitchRule *sw = new RTLIL::SwitchRule;
set_src_attr(sw, ast);
- sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap());
+ sw->signal = ast->children[0]->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap());
current_case->switches.push_back(sw);
for (auto &attr : ast->attributes) {
@@ -634,7 +641,7 @@ struct AST_INTERNAL::ProcessGenerator
else if (node->type == AST_BLOCK)
processAst(node);
else
- current_case->compare.push_back(node->genWidthRTLIL(sw->signal.size(), &subst_rvalue_map.stdmap()));
+ current_case->compare.push_back(node->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap()));
}
if (default_case != current_case)
sw->cases.push_back(current_case);
@@ -698,8 +705,99 @@ struct AST_INTERNAL::ProcessGenerator
log_abort();
}
}
+
+ void processMemWrites(RTLIL::SyncRule *sync)
+ {
+ // Maps per-memid AST_MEMWR IDs to indices in the mem_write_actions array.
+ dict<std::pair<std::string, int>, int> port_map;
+ for (auto child : always->children)
+ if (child->type == AST_MEMWR)
+ {
+ std::string memid = child->str;
+ int portid = child->children[3]->asInt(false);
+ int cur_idx = GetSize(sync->mem_write_actions);
+ RTLIL::MemWriteAction action;
+ set_src_attr(&action, child);
+ action.memid = memid;
+ action.address = child->children[0]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap());
+ action.data = child->children[1]->genWidthRTLIL(current_module->memories[memid]->width, true, &subst_rvalue_map.stdmap());
+ action.enable = child->children[2]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap());
+ RTLIL::Const orig_priority_mask = child->children[4]->bitsAsConst();
+ RTLIL::Const priority_mask = RTLIL::Const(0, cur_idx);
+ for (int i = 0; i < portid; i++) {
+ int new_bit = port_map[std::make_pair(memid, i)];
+ priority_mask.bits[new_bit] = orig_priority_mask.bits[i];
+ }
+ action.priority_mask = priority_mask;
+ sync->mem_write_actions.push_back(action);
+ port_map[std::make_pair(memid, portid)] = cur_idx;
+ }
+ }
};
+// Generate RTLIL for a bind construct
+//
+// The AST node will have one or more AST_IDENTIFIER children, which were added
+// by bind_target_instance in the parser. After these, it will have one or more
+// cells, as parsed by single_cell. These have type AST_CELL.
+//
+// If there is more than one AST_IDENTIFIER, the first one should be considered
+// a module identifier. If there is only one AST_IDENTIFIER, we can't tell at
+// this point whether it's a module/interface name or the name of an instance
+// because the correct interpretation depends on what's visible at elaboration
+// time. For now, we just treat it as a target instance with unknown type, and
+// we'll deal with the corner case in the hierarchy pass.
+//
+// To simplify downstream code, RTLIL::Binding only has a single target and
+// single bound instance. If we see the syntax that allows more than one of
+// either, we split it into multiple Binding objects.
+std::vector<RTLIL::Binding *> AstNode::genBindings() const
+{
+ // Partition children into identifiers and cells
+ int num_ids = 0;
+ for (int i = 0; i < GetSize(children); ++i) {
+ if (children[i]->type != AST_IDENTIFIER) {
+ log_assert(i > 0);
+ num_ids = i;
+ break;
+ }
+ }
+
+ // We should have found at least one child that's not an identifier
+ log_assert(num_ids > 0);
+
+ // Make sense of the identifiers, extracting a possible type name and a
+ // list of hierarchical IDs. We represent an unknown type with an empty
+ // string.
+ RTLIL::IdString tgt_type;
+ int first_tgt_inst = 0;
+ if (num_ids > 1) {
+ tgt_type = children[0]->str;
+ first_tgt_inst = 1;
+ }
+
+ std::vector<RTLIL::Binding *> ret;
+
+ // At this point, we know that children with index >= first_tgt_inst and
+ // index < num_ids are (hierarchical?) names of target instances. Make a
+ // binding object for each of them, and fill in the generated instance
+ // cells each time.
+ for (int i = first_tgt_inst; i < num_ids; ++i) {
+ const AstNode &tgt_child = *children[i];
+
+ for (int j = num_ids; j < GetSize(children); ++j) {
+ const AstNode &cell_child = *children[j];
+
+ log_assert(cell_child.type == AST_CELL);
+
+ ret.push_back(new AST::Binding(tgt_type, tgt_child.str,
+ cell_child));
+ }
+ }
+
+ return ret;
+}
+
// detect sign and width of an expression
void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real)
{
@@ -732,8 +830,15 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
case AST_IDENTIFIER:
id_ast = id2ast;
- if (id_ast == NULL && current_scope.count(str))
- id_ast = current_scope.at(str);
+ if (!id_ast) {
+ if (current_scope.count(str))
+ id_ast = current_scope[str];
+ else {
+ std::string alt = try_pop_module_prefix();
+ if (current_scope.count(alt))
+ id_ast = current_scope[alt];
+ }
+ }
if (!id_ast)
log_file_error(filename, location.first_line, "Failed to resolve identifier %s for width detection!\n", str.c_str());
if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) {
@@ -772,6 +877,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
if (children.size() > 1)
range = children[1];
+ } else if (id_ast->type == AST_STRUCT_ITEM) {
+ AstNode *tmp_range = make_struct_member_range(this, id_ast);
+ this_width = tmp_range->range_left - tmp_range->range_right + 1;
+ delete tmp_range;
} else
log_file_error(filename, location.first_line, "Failed to detect width for identifier %s!\n", str.c_str());
if (range) {
@@ -823,7 +932,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (children.at(0)->type != AST_CONSTANT)
log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");
children.at(1)->detectSignWidthWorker(width_hint, sign_hint);
- width_hint = children.at(0)->bitsAsConst().as_int();
+ this_width = children.at(0)->bitsAsConst().as_int();
+ width_hint = max(width_hint, this_width);
if (width_hint <= 0)
log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");
break;
@@ -923,6 +1033,40 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
width_hint = max(width_hint, this_width);
break;
+ case AST_CASE:
+ {
+ // This detects the _overall_ sign and width to be used for comparing
+ // the case expression with the case item expressions. The case
+ // expression and case item expressions are extended to the maximum
+ // width among them, and are only interpreted as signed if all of them
+ // are signed.
+ width_hint = -1;
+ sign_hint = true;
+ auto visit_case_expr = [&width_hint, &sign_hint] (AstNode *node) {
+ int sub_width_hint = -1;
+ bool sub_sign_hint = true;
+ node->detectSignWidth(sub_width_hint, sub_sign_hint);
+ width_hint = max(width_hint, sub_width_hint);
+ sign_hint &= sub_sign_hint;
+ };
+ visit_case_expr(children[0]);
+ for (size_t i = 1; i < children.size(); i++) {
+ AstNode *child = children[i];
+ for (AstNode *v : child->children)
+ if (v->type != AST_DEFAULT && v->type != AST_BLOCK)
+ visit_case_expr(v);
+ }
+ break;
+ }
+
+ case AST_PREFIX:
+ // Prefix nodes always resolve to identifiers in generate loops, so we
+ // can simply perform the resolution to determine the sign and width.
+ simplify(true, false, false, 1, -1, false, false);
+ log_assert(type == AST_IDENTIFIER);
+ detectSignWidthWorker(width_hint, sign_hint, found_real);
+ break;
+
case AST_FCALL:
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
if (GetSize(children) == 1) {
@@ -944,6 +1088,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
}
break;
}
+ if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") {
+ width_hint = 32;
+ sign_hint = true;
+ break;
+ }
if (current_scope.count(str))
{
// This width detection is needed for function calls which are
@@ -983,8 +1132,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
// everything should have been handled above -> print error if not.
default:
+ AstNode *current_scope_ast = current_ast_mod == nullptr ? current_ast : current_ast_mod;
for (auto f : log_files)
- current_ast_mod->dumpAst(f, "verilog-ast> ");
+ current_scope_ast->dumpAst(f, "verilog-ast> ");
log_file_error(filename, location.first_line, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
}
@@ -1000,6 +1150,12 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real
if (found_real)
*found_real = false;
detectSignWidthWorker(width_hint, sign_hint, found_real);
+
+ constexpr int kWidthLimit = 1 << 24;
+ if (width_hint >= kWidthLimit)
+ log_file_error(filename, location.first_line,
+ "Expression width %d exceeds implementation limit of %d!\n",
+ width_hint, kWidthLimit);
}
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
@@ -1227,7 +1383,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
RTLIL::Wire *wire = current_module->addWire(str);
set_src_attr(wire, this);
wire->name = str;
- if (flag_autowire)
+
+ // If we are currently processing a bind directive which wires up
+ // signals or parameters explicitly, rather than with .*, then
+ // current_module will start out empty and we don't want to warn the
+ // user about it: we'll spot broken wiring later, when we run the
+ // hierarchy pass.
+ if (dynamic_cast<RTLIL::Binding*>(current_module)) {
+ /* nothing to do here */
+ } else if (flag_autowire)
log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str());
else
log_file_error(filename, location.first_line, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
@@ -1367,13 +1531,20 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// changing the size of signal can be done directly using RTLIL::SigSpec
case AST_CAST_SIZE: {
RTLIL::SigSpec size = children[0]->genRTLIL();
- RTLIL::SigSpec sig = children[1]->genRTLIL();
if (!size.is_fully_const())
log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");
int width = size.as_int();
if (width <= 0)
log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");
- sig.extend_u0(width, sign_hint);
+ // determine the *signedness* of the expression
+ int sub_width_hint = -1;
+ bool sub_sign_hint = true;
+ children[1]->detectSignWidth(sub_width_hint, sub_sign_hint);
+ // generate the signal given the *cast's* size and the
+ // *expression's* signedness
+ RTLIL::SigSpec sig = children[1]->genWidthRTLIL(width, sub_sign_hint);
+ // context may effect this node's signedness, but not that of the
+ // casted expression
is_signed = sign_hint;
return sig;
}
@@ -1638,43 +1809,35 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
return RTLIL::SigSpec(wire);
}
- // generate $memwr cells for memory write ports
- case AST_MEMWR:
+ // generate $meminit cells
case AST_MEMINIT:
{
std::stringstream sstr;
- sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
+ sstr << "$meminit$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
+
+ SigSpec en_sig = children[2]->genRTLIL();
- RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? ID($memwr) : ID($meminit));
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($meminit_v2));
set_src_attr(cell, this);
int mem_width, mem_size, addr_bits;
id2ast->meminfo(mem_width, mem_size, addr_bits);
- int num_words = 1;
- if (type == AST_MEMINIT) {
- if (children[2]->type != AST_CONSTANT)
- log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n");
- num_words = int(children[2]->asInt(false));
- cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
- }
+ if (children[3]->type != AST_CONSTANT)
+ log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n");
+ int num_words = int(children[3]->asInt(false));
+ cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
SigSpec addr_sig = children[0]->genRTLIL();
cell->setPort(ID::ADDR, addr_sig);
- cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words));
+ cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words, true));
+ cell->setPort(ID::EN, en_sig);
cell->parameters[ID::MEMID] = RTLIL::Const(str);
cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig));
cell->parameters[ID::WIDTH] = RTLIL::Const(current_module->memories[str]->width);
- if (type == AST_MEMWR) {
- cell->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::Sx, 1));
- cell->setPort(ID::EN, children[2]->genRTLIL());
- cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(0);
- cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(0);
- }
-
cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1);
}
break;
@@ -1728,7 +1891,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_ASSIGN:
{
RTLIL::SigSpec left = children[0]->genRTLIL();
- RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size());
+ RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size(), true);
if (left.has_const()) {
RTLIL::SigSpec new_left, new_right;
for (int i = 0; i < GetSize(left); i++)
@@ -1768,21 +1931,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
continue;
}
if (child->type == AST_PARASET) {
- int extra_const_flags = 0;
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
- if (child->children[0]->type == AST_REALVALUE) {
+ const AstNode *value = child->children[0];
+ if (value->type == AST_REALVALUE)
log_file_warning(filename, location.first_line, "Replacing floating point parameter %s.%s = %f with string.\n",
- log_id(cell), log_id(paraname), child->children[0]->realvalue);
- extra_const_flags = RTLIL::CONST_FLAG_REAL;
- auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
- strnode->cloneInto(child->children[0]);
- delete strnode;
- }
- if (child->children[0]->type != AST_CONSTANT)
+ log_id(cell), log_id(paraname), value->realvalue);
+ else if (value->type != AST_CONSTANT)
log_file_error(filename, location.first_line, "Parameter %s.%s with non-constant value!\n",
log_id(cell), log_id(paraname));
- cell->parameters[paraname] = child->children[0]->asParaConst();
- cell->parameters[paraname].flags |= extra_const_flags;
+ cell->parameters[paraname] = value->asParaConst();
continue;
}
if (child->type == AST_ARGUMENT) {
@@ -1799,7 +1956,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (sig.is_wire()) {
// if the resulting SigSpec is a wire, its
// signedness should match that of the AstNode
- log_assert(arg->is_signed == sig.as_wire()->is_signed);
+ if (arg->type == AST_IDENTIFIER && arg->id2ast && arg->id2ast->is_signed && !arg->is_signed)
+ // fully-sliced signed wire will be resolved
+ // once the module becomes available
+ log_assert(attributes.count(ID::reprocess_after));
+ else
+ log_assert(arg->is_signed == sig.as_wire()->is_signed);
} else if (arg->is_signed) {
// non-trivial signed nodes are indirected through
// signed wires to enable sign extension
@@ -1898,6 +2060,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
} break;
+ case AST_BIND: {
+ // Read a bind construct. This should have one or more cells as children.
+ for (RTLIL::Binding *binding : genBindings())
+ current_module->add(binding);
+ break;
+ }
+
case AST_FCALL: {
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq")
{
@@ -1943,8 +2112,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
default:
for (auto f : log_files)
current_ast_mod->dumpAst(f, "verilog-ast> ");
- type_name = type2str(type);
- log_file_error(filename, location.first_line, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str());
+ log_file_error(filename, location.first_line, "Don't know how to generate RTLIL code for %s node!\n", type2str(type).c_str());
}
return RTLIL::SigSpec();
@@ -1953,14 +2121,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or
// signals must be substituted before being used as input values (used by ProcessGenerator)
// note that this is using some global variables to communicate this special settings to AstNode::genRTLIL().
-RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr)
+RTLIL::SigSpec AstNode::genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr)
{
const dict<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr;
if (new_subst_ptr)
genRTLIL_subst_ptr = new_subst_ptr;
- bool sign_hint = true;
+ bool sign_hint = sgn;
int width_hint = width;
detectSignWidthWorker(width_hint, sign_hint);
RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint);
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 5c4dd290f..18b1e1e11 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -192,6 +192,7 @@ void AstNode::annotateTypedEnums(AstNode *template_node)
log_assert(current_scope.count(enum_type) == 1);
AstNode *enum_node = current_scope.at(enum_type);
log_assert(enum_node->type == AST_ENUM);
+ while (enum_node->simplify(true, false, false, 1, -1, false, true)) { }
//get width from 1st enum item:
log_assert(enum_node->children.size() >= 1);
AstNode *enum_item0 = enum_node->children[0];
@@ -330,6 +331,8 @@ static int size_packed_struct(AstNode *snode, int base_offset)
}
}
// range nodes are now redundant
+ for (AstNode *child : node->children)
+ delete child;
node->children.clear();
}
else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) {
@@ -344,6 +347,8 @@ static int size_packed_struct(AstNode *snode, int base_offset)
save_struct_array_width(node, width);
width *= array_count;
// range nodes are now redundant
+ for (AstNode *child : node->children)
+ delete child;
node->children.clear();
}
else if (node->range_left < 0) {
@@ -451,7 +456,7 @@ static AstNode *slice_range(AstNode *rnode, AstNode *snode)
}
-static AstNode *make_struct_member_range(AstNode *node, AstNode *member_node)
+AstNode *AST::make_struct_member_range(AstNode *node, AstNode *member_node)
{
// Work out the range in the packed array that corresponds to a struct member
// taking into account any range operations applicable to the current node
@@ -559,6 +564,227 @@ static std::string prefix_id(const std::string &prefix, const std::string &str)
return prefix + str;
}
+// direct access to this global should be limited to the following two functions
+static const RTLIL::Design *simplify_design_context = nullptr;
+
+void AST::set_simplify_design_context(const RTLIL::Design *design)
+{
+ log_assert(!simplify_design_context || !design);
+ simplify_design_context = design;
+}
+
+// lookup the module with the given name in the current design context
+static const RTLIL::Module* lookup_module(const std::string &name)
+{
+ return simplify_design_context->module(name);
+}
+
+const RTLIL::Module* AstNode::lookup_cell_module()
+{
+ log_assert(type == AST_CELL);
+
+ auto reprocess_after = [this] (const std::string &modname) {
+ if (!attributes.count(ID::reprocess_after))
+ attributes[ID::reprocess_after] = AstNode::mkconst_str(modname);
+ };
+
+ const AstNode *celltype = nullptr;
+ for (const AstNode *child : children)
+ if (child->type == AST_CELLTYPE) {
+ celltype = child;
+ break;
+ }
+ log_assert(celltype != nullptr);
+
+ const RTLIL::Module *module = lookup_module(celltype->str);
+ if (!module)
+ module = lookup_module("$abstract" + celltype->str);
+ if (!module) {
+ if (celltype->str.at(0) != '$')
+ reprocess_after(celltype->str);
+ return nullptr;
+ }
+
+ // build a mapping from true param name to param value
+ size_t para_counter = 0;
+ dict<RTLIL::IdString, RTLIL::Const> cell_params_map;
+ for (AstNode *child : children) {
+ if (child->type != AST_PARASET)
+ continue;
+
+ if (child->str.empty() && para_counter >= module->avail_parameters.size())
+ return nullptr; // let hierarchy handle this error
+ IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str;
+
+ const AstNode *value = child->children[0];
+ if (value->type != AST_REALVALUE && value->type != AST_CONSTANT)
+ return nullptr; // let genrtlil handle this error
+ cell_params_map[paraname] = value->asParaConst();
+ }
+
+ // put the parameters in order and generate the derived module name
+ std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters;
+ for (RTLIL::IdString param : module->avail_parameters) {
+ auto it = cell_params_map.find(param);
+ if (it != cell_params_map.end())
+ named_parameters.emplace_back(it->first, it->second);
+ }
+ std::string modname = celltype->str;
+ if (cell_params_map.size()) // not named_parameters to cover hierarchical defparams
+ modname = derived_module_name(celltype->str, named_parameters);
+
+ // try to find the resolved module
+ module = lookup_module(modname);
+ if (!module) {
+ reprocess_after(modname);
+ return nullptr;
+ }
+ return module;
+}
+
+// returns whether an expression contains an unbased unsized literal; does not
+// check the literal exists in a self-determined context
+static bool contains_unbased_unsized(const AstNode *node)
+{
+ if (node->type == AST_CONSTANT)
+ return node->is_unsized;
+ for (const AstNode *child : node->children)
+ if (contains_unbased_unsized(child))
+ return true;
+ return false;
+}
+
+// adds a wire to the current module with the given name that matches the
+// dimensions of the given wire reference
+void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str)
+{
+ AstNode *left = AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true);
+ AstNode *right = AstNode::mkconst_int(ref->start_offset, true);
+ if (ref->upto)
+ std::swap(left, right);
+ AstNode *range = new AstNode(AST_RANGE, left, right);
+
+ AstNode *wire = new AstNode(AST_WIRE, range);
+ wire->is_signed = ref->is_signed;
+ wire->is_logic = true;
+ wire->str = str;
+
+ current_ast_mod->children.push_back(wire);
+ current_scope[str] = wire;
+}
+
+enum class IdentUsage {
+ NotReferenced, // target variable is neither read or written in the block
+ Assigned, // target variable is always assigned before use
+ SyncRequired, // target variable may be used before it has been assigned
+};
+
+// determines whether a local variable a block is always assigned before it is
+// used, meaning the nosync attribute can automatically be added to that
+// variable
+static IdentUsage always_asgn_before_use(const AstNode *node, const std::string &target)
+{
+ // This variable has been referenced before it has necessarily been assigned
+ // a value in this procedure.
+ if (node->type == AST_IDENTIFIER && node->str == target)
+ return IdentUsage::SyncRequired;
+
+ // For case statements (which are also used for if/else), we check each
+ // possible branch. If the variable is assigned in all branches, then it is
+ // assigned, and a sync isn't required. If it used before assignment in any
+ // branch, then a sync is required.
+ if (node->type == AST_CASE) {
+ bool all_defined = true;
+ bool any_used = false;
+ bool has_default = false;
+ for (const AstNode *child : node->children) {
+ if (child->type == AST_COND && child->children.at(0)->type == AST_DEFAULT)
+ has_default = true;
+ IdentUsage nested = always_asgn_before_use(child, target);
+ if (nested != IdentUsage::Assigned && child->type == AST_COND)
+ all_defined = false;
+ if (nested == IdentUsage::SyncRequired)
+ any_used = true;
+ }
+ if (any_used)
+ return IdentUsage::SyncRequired;
+ else if (all_defined && has_default)
+ return IdentUsage::Assigned;
+ else
+ return IdentUsage::NotReferenced;
+ }
+
+ // Check if this is an assignment to the target variable. For simplicity, we
+ // don't analyze sub-ranges of the variable.
+ if (node->type == AST_ASSIGN_EQ) {
+ const AstNode *ident = node->children.at(0);
+ if (ident->type == AST_IDENTIFIER && ident->str == target)
+ return IdentUsage::Assigned;
+ }
+
+ for (const AstNode *child : node->children) {
+ IdentUsage nested = always_asgn_before_use(child, target);
+ if (nested != IdentUsage::NotReferenced)
+ return nested;
+ }
+ return IdentUsage::NotReferenced;
+}
+
+static const std::string auto_nosync_prefix = "\\AutoNosync";
+
+// mark a local variable in an always_comb block for automatic nosync
+// consideration
+static void mark_auto_nosync(AstNode *block, const AstNode *wire)
+{
+ log_assert(block->type == AST_BLOCK);
+ log_assert(wire->type == AST_WIRE);
+ block->attributes[auto_nosync_prefix + wire->str] = AstNode::mkconst_int(1,
+ false);
+}
+
+// check a procedural block for auto-nosync markings, remove them, and add
+// nosync to local variables as necessary
+static void check_auto_nosync(AstNode *node)
+{
+ std::vector<RTLIL::IdString> attrs_to_drop;
+ for (const auto& elem : node->attributes) {
+ // skip attributes that don't begin with the prefix
+ if (elem.first.compare(0, auto_nosync_prefix.size(),
+ auto_nosync_prefix.c_str()))
+ continue;
+
+ // delete and remove the attribute once we're done iterating
+ attrs_to_drop.push_back(elem.first);
+
+ // find the wire based on the attribute
+ std::string wire_name = elem.first.substr(auto_nosync_prefix.size());
+ auto it = current_scope.find(wire_name);
+ if (it == current_scope.end())
+ continue;
+
+ // analyze the usage of the local variable in this block
+ IdentUsage ident_usage = always_asgn_before_use(node, wire_name);
+ if (ident_usage != IdentUsage::Assigned)
+ continue;
+
+ // mark the wire with `nosync`
+ AstNode *wire = it->second;
+ log_assert(wire->type == AST_WIRE);
+ wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
+ }
+
+ // remove the attributes we've "consumed"
+ for (const RTLIL::IdString &str : attrs_to_drop) {
+ auto it = node->attributes.find(str);
+ delete it->second;
+ node->attributes.erase(it);
+ }
+
+ // check local variables in any nested blocks
+ for (AstNode *child : node->children)
+ check_auto_nosync(child);
+}
+
// convert the AST into a simpler AST that has all parameters substituted by their
// values, unrolled for-loops, expanded generate blocks, etc. when this function
// is done with an AST it can be converted into RTLIL using genRTLIL().
@@ -866,6 +1092,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
}
+
+ for (AstNode *child : children)
+ if (child->type == AST_ALWAYS &&
+ child->attributes.count(ID::always_comb))
+ check_auto_nosync(child);
}
// create name resolution entries for all objects with names
@@ -874,7 +1105,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[i];
// these nodes appear at the top level in a package and can define names
- if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF) {
+ if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF || node->type == AST_FUNCTION || node->type == AST_TASK) {
current_scope[node->str] = node;
}
if (node->type == AST_ENUM) {
@@ -915,6 +1146,113 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
+ if (type == AST_CELL) {
+ bool lookup_suggested = false;
+
+ for (AstNode *child : children) {
+ // simplify any parameters to constants
+ if (child->type == AST_PARASET)
+ while (child->simplify(true, false, false, 1, -1, false, true)) { }
+
+ // look for patterns which _may_ indicate ambiguity requiring
+ // resolution of the underlying module
+ if (child->type == AST_ARGUMENT) {
+ if (child->children.size() != 1)
+ continue;
+ const AstNode *value = child->children[0];
+ if (value->type == AST_IDENTIFIER) {
+ const AstNode *elem = value->id2ast;
+ if (elem == nullptr) {
+ if (current_scope.count(value->str))
+ elem = current_scope.at(value->str);
+ else
+ continue;
+ }
+ if (elem->type == AST_MEMORY)
+ // need to determine is the is a read or wire
+ lookup_suggested = true;
+ else if (elem->type == AST_WIRE && elem->is_signed && !value->children.empty())
+ // this may be a fully sliced signed wire which needs
+ // to be indirected to produce an unsigned connection
+ lookup_suggested = true;
+ }
+ else if (contains_unbased_unsized(value))
+ // unbased unsized literals extend to width of the context
+ lookup_suggested = true;
+ }
+ }
+
+ const RTLIL::Module *module = nullptr;
+ if (lookup_suggested)
+ module = lookup_cell_module();
+ if (module) {
+ size_t port_counter = 0;
+ for (AstNode *child : children) {
+ if (child->type != AST_ARGUMENT)
+ continue;
+
+ // determine the full name of port this argument is connected to
+ RTLIL::IdString port_name;
+ if (child->str.size())
+ port_name = child->str;
+ else {
+ if (port_counter >= module->ports.size())
+ log_file_error(filename, location.first_line,
+ "Cell instance has more ports than the module!\n");
+ port_name = module->ports[port_counter++];
+ }
+
+ // find the port's wire in the underlying module
+ const RTLIL::Wire *ref = module->wire(port_name);
+ if (ref == nullptr)
+ log_file_error(filename, location.first_line,
+ "Cell instance refers to port %s which does not exist in module %s!.\n",
+ log_id(port_name), log_id(module->name));
+
+ // select the argument, if present
+ log_assert(child->children.size() <= 1);
+ if (child->children.empty())
+ continue;
+ AstNode *arg = child->children[0];
+
+ // plain identifiers never need indirection; this also prevents
+ // adding infinite levels of indirection
+ if (arg->type == AST_IDENTIFIER && arg->children.empty())
+ continue;
+
+ // only add indirection for standard inputs or outputs
+ if (ref->port_input == ref->port_output)
+ continue;
+
+ did_something = true;
+
+ // create the indirection wire
+ std::stringstream sstr;
+ sstr << "$indirect$" << ref->name.c_str() << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
+ std::string tmp_str = sstr.str();
+ add_wire_for_ref(ref, tmp_str);
+
+ AstNode *asgn = new AstNode(AST_ASSIGN);
+ current_ast_mod->children.push_back(asgn);
+
+ AstNode *ident = new AstNode(AST_IDENTIFIER);
+ ident->str = tmp_str;
+ child->children[0] = ident->clone();
+
+ if (ref->port_input && !ref->port_output) {
+ asgn->children.push_back(ident);
+ asgn->children.push_back(arg);
+ } else {
+ log_assert(!ref->port_input && ref->port_output);
+ asgn->children.push_back(arg);
+ asgn->children.push_back(ident);
+ }
+ }
+
+
+ }
+ }
+
int backup_width_hint = width_hint;
bool backup_sign_hint = sign_hint;
@@ -1168,8 +1506,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (const_fold && type == AST_CASE)
{
+ detectSignWidth(width_hint, sign_hint);
while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }
if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) {
+ RTLIL::Const case_expr = children[0]->bitsAsConst(width_hint, sign_hint);
std::vector<AstNode*> new_children;
new_children.push_back(children[0]);
for (int i = 1; i < GetSize(children); i++) {
@@ -1182,7 +1522,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
continue;
while (v->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }
if (v->type == AST_CONSTANT && v->bits_only_01()) {
- if (v->bits == children[0]->bits) {
+ RTLIL::Const case_item_expr = v->bitsAsConst(width_hint, sign_hint);
+ RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1);
+ log_assert(match.bits.size() == 1);
+ if (match.bits.front() == RTLIL::State::S1) {
while (i+1 < GetSize(children))
delete children[++i];
goto keep_const_cond;
@@ -1201,6 +1544,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
+ dict<std::string, pool<int>> backup_memwr_visible;
+ dict<std::string, pool<int>> final_memwr_visible;
+
+ if (type == AST_CASE && stage == 2) {
+ backup_memwr_visible = current_memwr_visible;
+ final_memwr_visible = current_memwr_visible;
+ }
+
// simplify all children first
// (iterate by index as e.g. auto wires can add new children in the process)
for (size_t i = 0; i < children.size(); i++) {
@@ -1263,11 +1614,25 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
flag_autowire = backup_flag_autowire;
unevaluated_tern_branch = backup_unevaluated_tern_branch;
+ if (stage == 2 && type == AST_CASE) {
+ for (auto &x : current_memwr_visible) {
+ for (int y : x.second)
+ final_memwr_visible[x.first].insert(y);
+ }
+ current_memwr_visible = backup_memwr_visible;
+ }
}
for (auto &attr : attributes) {
while (attr.second->simplify(true, false, false, stage, -1, false, true))
did_something = true;
}
+ if (type == AST_CASE && stage == 2) {
+ current_memwr_visible = final_memwr_visible;
+ }
+ if (type == AST_ALWAYS && stage == 2) {
+ current_memwr_visible.clear();
+ current_memwr_count.clear();
+ }
if (reset_width_after_children) {
width_hint = backup_width_hint;
@@ -1363,6 +1728,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) {
// replace with wire representing the packed structure
newNode = make_packed_struct(template_node, str);
+ newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
// add original input/output attribute to resolved wire
newNode->is_input = this->is_input;
newNode->is_output = this->is_output;
@@ -1411,18 +1777,33 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (is_custom_type) {
log_assert(children.size() == 2);
log_assert(children[1]->type == AST_WIRETYPE);
- if (!current_scope.count(children[1]->str))
- log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
- AstNode *resolved_type_node = current_scope.at(children[1]->str);
+ auto type_name = children[1]->str;
+ if (!current_scope.count(type_name)) {
+ log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str());
+ }
+ AstNode *resolved_type_node = current_scope.at(type_name);
if (resolved_type_node->type != AST_TYPEDEF)
- log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str());
+ log_file_error(filename, location.first_line, "`%s' does not name a type\n", type_name.c_str());
log_assert(resolved_type_node->children.size() == 1);
AstNode *template_node = resolved_type_node->children[0];
- delete children[1];
- children.pop_back();
// Ensure typedef itself is fully simplified
- while(template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+ while (template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+
+ if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) {
+ // replace with wire representing the packed structure
+ newNode = make_packed_struct(template_node, str);
+ newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
+ newNode->type = type;
+ current_scope[str] = this;
+ // copy param value, it needs to be 1st value
+ delete children[1];
+ children.pop_back();
+ newNode->children.insert(newNode->children.begin(), children[0]->clone());
+ goto apply_newNode;
+ }
+ delete children[1];
+ children.pop_back();
if (template_node->type == AST_MEMORY)
log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
@@ -1643,6 +2024,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
newNode = new AstNode(AST_IDENTIFIER, range);
newNode->str = sname;
newNode->basic_prep = true;
+ if (item_node->is_signed)
+ newNode = new AstNode(AST_TO_SIGNED, newNode);
goto apply_newNode;
}
}
@@ -1652,13 +2035,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_IDENTIFIER) {
if (current_scope.count(str) == 0) {
AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
- const std::string& mod_scope = current_scope_ast->str;
- if (str[0] == '\\' && str.substr(0, mod_scope.size()) == mod_scope) {
- std::string new_str = "\\" + str.substr(mod_scope.size() + 1);
- if (current_scope.count(new_str)) {
- str = new_str;
- }
- }
+ str = try_pop_module_prefix();
for (auto node : current_scope_ast->children) {
//log("looking at mod scope child %s\n", type2str(node->type).c_str());
switch (node->type) {
@@ -1712,7 +2089,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
// split memory access with bit select to individual statements
- if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue)
+ if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue && stage == 2)
{
if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)
log_file_error(filename, location.first_line, "Invalid bit-select on memory access!\n");
@@ -1964,6 +2341,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
{
expand_genblock(str + ".");
+ // if this is an autonamed block is in an always_comb
+ if (current_always && current_always->attributes.count(ID::always_comb)
+ && str.at(0) == '$')
+ // track local variables in this block so we can consider adding
+ // nosync once the block has been fully elaborated
+ for (AstNode *child : children)
+ if (child->type == AST_WIRE &&
+ !child->attributes.count(ID::nosync))
+ mark_auto_nosync(this, child);
+
std::vector<AstNode*> new_children;
for (size_t i = 0; i < children.size(); i++)
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) {
@@ -2180,6 +2567,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
children.push_back(node);
did_something = true;
}
+ else if (str == "buf" || str == "not")
+ {
+ AstNode *input = children_list.back();
+ if (str == "not")
+ input = new AstNode(AST_BIT_NOT, input);
+
+ newNode = new AstNode(AST_GENBLOCK);
+ for (auto it = children_list.begin(); it != std::prev(children_list.end()); it++) {
+ newNode->children.push_back(new AstNode(AST_ASSIGN, *it, input->clone()));
+ newNode->children.back()->was_checked = true;
+ }
+ delete input;
+
+ did_something = true;
+ }
else
{
AstNodeType op_type = AST_NONE;
@@ -2197,10 +2599,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
op_type = AST_BIT_XOR;
if (str == "xnor")
op_type = AST_BIT_XOR, invert_results = true;
- if (str == "buf")
- op_type = AST_POS;
- if (str == "not")
- op_type = AST_POS, invert_results = true;
log_assert(op_type != AST_NONE);
AstNode *node = children_list[1];
@@ -2252,6 +2650,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
+ delete left_at_zero_ast;
+ delete right_at_zero_ast;
}
bool use_case_method = false;
@@ -2554,12 +2954,12 @@ skip_dynamic_range_lvalue_expansion:;
current_scope[wire_addr->str] = wire_addr;
while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
- AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
+ AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
assign_addr->children[0]->str = id_addr;
assign_addr->children[0]->was_checked = true;
defNode->children.push_back(assign_addr);
- assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
+ assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
assign_addr->children[0]->str = id_addr;
assign_addr->children[0]->was_checked = true;
newNode->children.push_back(assign_addr);
@@ -2580,7 +2980,7 @@ skip_dynamic_range_lvalue_expansion:;
current_scope[wire_data->str] = wire_data;
while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
- AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
+ AstNode *assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
assign_data->children[0]->str = id_data;
assign_data->children[0]->was_checked = true;
defNode->children.push_back(assign_data);
@@ -2589,25 +2989,20 @@ skip_dynamic_range_lvalue_expansion:;
node_data->str = id_data;
}
- AstNode *node_en = nullptr;
- if (current_always->type == AST_INITIAL) {
- node_en = AstNode::mkconst_int(1, false);
- } else {
- AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
- wire_en->str = id_en;
- wire_en->was_checked = true;
- current_ast_mod->children.push_back(wire_en);
- current_scope[wire_en->str] = wire_en;
- while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
-
- AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
- assign_en->children[0]->str = id_en;
- assign_en->children[0]->was_checked = true;
- defNode->children.push_back(assign_en);
+ AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_en->str = id_en;
+ wire_en->was_checked = true;
+ current_ast_mod->children.push_back(wire_en);
+ current_scope[wire_en->str] = wire_en;
+ while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
- node_en = new AstNode(AST_IDENTIFIER);
- node_en->str = id_en;
- }
+ AstNode *assign_en_first = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
+ assign_en_first->children[0]->str = id_en;
+ assign_en_first->children[0]->was_checked = true;
+ defNode->children.push_back(assign_en_first);
+
+ AstNode *node_en = new AstNode(AST_IDENTIFIER);
+ node_en->str = id_en;
if (!defNode->children.empty())
current_top_block->children.insert(current_top_block->children.begin(), defNode);
@@ -2626,18 +3021,16 @@ skip_dynamic_range_lvalue_expansion:;
std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx);
- assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone()));
assign_data->children[0]->str = id_data;
assign_data->children[0]->was_checked = true;
- if (current_always->type != AST_INITIAL) {
- for (int i = 0; i < mem_width; i++)
- set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
- assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
- assign_en->children[0]->str = id_en;
- assign_en->children[0]->was_checked = true;
- }
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
+ assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+ assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
}
else
{
@@ -2655,19 +3048,17 @@ skip_dynamic_range_lvalue_expansion:;
log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
- assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
assign_data->children[0]->str = id_data;
assign_data->children[0]->was_checked = true;
- if (current_always->type != AST_INITIAL) {
- for (int i = 0; i < mem_width; i++)
- set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
- assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
- new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
- assign_en->children[0]->str = id_en;
- assign_en->children[0]->was_checked = true;
- }
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
+ assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
+ new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
+ assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
delete left_at_zero_ast;
delete right_at_zero_ast;
@@ -2677,26 +3068,42 @@ skip_dynamic_range_lvalue_expansion:;
else
{
if (!(children[0]->children.size() == 1 && children[1]->isConst())) {
- assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
+ assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[1]->clone());
assign_data->children[0]->str = id_data;
assign_data->children[0]->was_checked = true;
}
- if (current_always->type != AST_INITIAL) {
- assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
- assign_en->children[0]->str = id_en;
- assign_en->children[0]->was_checked = true;
- }
+ assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+ assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
}
if (assign_data)
newNode->children.push_back(assign_data);
if (assign_en)
newNode->children.push_back(assign_en);
- AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR, node_addr, node_data, node_en);
+ AstNode *wrnode;
+ if (current_always->type == AST_INITIAL)
+ wrnode = new AstNode(AST_MEMINIT, node_addr, node_data, node_en, mkconst_int(1, false));
+ else
+ wrnode = new AstNode(AST_MEMWR, node_addr, node_data, node_en);
wrnode->str = children[0]->str;
wrnode->id2ast = children[0]->id2ast;
- current_ast_mod->children.push_back(wrnode);
+ wrnode->location = location;
+ if (wrnode->type == AST_MEMWR) {
+ int portid = current_memwr_count[wrnode->str]++;
+ wrnode->children.push_back(mkconst_int(portid, false));
+ std::vector<RTLIL::State> priority_mask;
+ for (int i = 0; i < portid; i++) {
+ bool has_prio = current_memwr_visible[wrnode->str].count(i);
+ priority_mask.push_back(State(has_prio));
+ }
+ wrnode->children.push_back(mkconst_bits(priority_mask, false));
+ current_memwr_visible[wrnode->str].insert(portid);
+ current_always->children.push_back(wrnode);
+ } else {
+ current_ast_mod->children.push_back(wrnode);
+ }
if (newNode->children.empty()) {
delete newNode;
@@ -3469,6 +3876,8 @@ skip_dynamic_range_lvalue_expansion:;
// convert purely constant arguments into localparams
if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child)) {
wire->type = AST_LOCALPARAM;
+ if (wire->attributes.count(ID::nosync))
+ delete wire->attributes.at(ID::nosync);
wire->attributes.erase(ID::nosync);
wire->children.insert(wire->children.begin(), arg->clone());
// args without a range implicitly have width 1
@@ -3492,6 +3901,7 @@ skip_dynamic_range_lvalue_expansion:;
}
// updates the sizing
while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+ delete arg;
continue;
}
AstNode *wire_id = new AstNode(AST_IDENTIFIER);
@@ -3773,7 +4183,11 @@ replace_fcall_later:;
case AST_CAST_SIZE:
if (children.at(0)->type == AST_CONSTANT && children.at(1)->type == AST_CONSTANT) {
int width = children[0]->bitsAsConst().as_int();
- RTLIL::Const val = children[1]->bitsAsConst(width);
+ RTLIL::Const val;
+ if (children[1]->is_unsized)
+ val = children[1]->bitsAsUnsizedConst(width);
+ else
+ val = children[1]->bitsAsConst(width);
newNode = mkconst_bits(val.bits, children[1]->is_signed);
}
break;
@@ -3841,8 +4255,12 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
AstNode *meminit = nullptr;
int next_meminit_cursor=0;
vector<State> meminit_bits;
+ vector<State> en_bits;
int meminit_size=0;
+ for (int i = 0; i < mem_width; i++)
+ en_bits.push_back(State::S1);
+
std::ifstream f;
f.open(mem_filename.c_str());
if (f.fail()) {
@@ -3916,12 +4334,13 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
{
if (meminit != nullptr) {
meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
- meminit->children[2] = AstNode::mkconst_int(meminit_size, false);
+ meminit->children[3] = AstNode::mkconst_int(meminit_size, false);
}
meminit = new AstNode(AST_MEMINIT);
meminit->children.push_back(AstNode::mkconst_int(cursor, false));
meminit->children.push_back(nullptr);
+ meminit->children.push_back(AstNode::mkconst_bits(en_bits, false));
meminit->children.push_back(nullptr);
meminit->str = memory->str;
meminit->id2ast = memory;
@@ -3956,7 +4375,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
if (meminit != nullptr) {
meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
- meminit->children[2] = AstNode::mkconst_int(meminit_size, false);
+ meminit->children[3] = AstNode::mkconst_int(meminit_size, false);
}
return block;
@@ -3967,7 +4386,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
// prefix is carried forward, but resolution of their children is deferred
void AstNode::expand_genblock(const std::string &prefix)
{
- if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) {
+ if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE || type == AST_PREFIX) {
log_assert(!str.empty());
// search starting in the innermost scope and then stepping outward
@@ -4053,10 +4472,15 @@ void AstNode::expand_genblock(const std::string &prefix)
for (size_t i = 0; i < children.size(); i++) {
AstNode *child = children[i];
- // AST_PREFIX member names should not be prefixed; a nested AST_PREFIX
- // still needs to recursed-into
- if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER)
+ // AST_PREFIX member names should not be prefixed; we recurse into them
+ // as normal to ensure indices and ranges are properly resolved, and
+ // then restore the previous string
+ if (type == AST_PREFIX && i == 1) {
+ std::string backup_scope_name = child->str;
+ child->expand_genblock(prefix);
+ child->str = backup_scope_name;
continue;
+ }
// functions/tasks may reference wires, constants, etc. in this scope
if (child->type == AST_FUNCTION || child->type == AST_TASK)
continue;
@@ -4301,10 +4725,12 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
log_assert(children[0]->type == AST_CONSTANT);
log_assert(children[1]->type == AST_CONSTANT);
log_assert(children[2]->type == AST_CONSTANT);
+ log_assert(children[3]->type == AST_CONSTANT);
int cursor = children[0]->asInt(false);
Const data = children[1]->bitsAsConst();
- int length = children[2]->asInt(false);
+ Const en = children[2]->bitsAsConst();
+ int length = children[3]->asInt(false);
if (length != 0)
{
@@ -4315,10 +4741,37 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
int wordsz = GetSize(data) / length;
for (int i = 0; i < length; i++) {
- block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false))), mkconst_bits(data.extract(i*wordsz, wordsz).bits, false)));
- block->children.back()->children[0]->str = str;
- block->children.back()->children[0]->id2ast = id2ast;
- block->children.back()->children[0]->was_checked = true;
+ int pos = 0;
+ while (pos < wordsz) {
+ if (en[pos] != State::S1) {
+ pos++;
+ } else {
+ int epos = pos + 1;
+ while (epos < wordsz && en[epos] == State::S1)
+ epos++;
+ int clen = epos - pos;
+ AstNode *range = new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false));
+ if (pos != 0 || epos != wordsz) {
+ int left;
+ int right;
+ AstNode *mrange = id2ast->children[0];
+ if (mrange->range_left < mrange->range_right) {
+ right = mrange->range_right - pos;
+ left = mrange->range_right - epos + 1;
+ } else {
+ right = mrange->range_right + pos;
+ left = mrange->range_right + epos - 1;
+ }
+ range = new AstNode(AST_MULTIRANGE, range, new AstNode(AST_RANGE, AstNode::mkconst_int(left, true), AstNode::mkconst_int(right, true)));
+ }
+ AstNode *target = new AstNode(AST_IDENTIFIER, range);
+ target->str = str;
+ target->id2ast = id2ast;
+ target->was_checked = true;
+ block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).bits, false)));
+ pos = epos;
+ }
+ }
}
}
@@ -4422,11 +4875,48 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
if (children[0]->children[0]->type == AST_CONSTANT)
{
int id = children[0]->children[0]->integer;
- str = stringf("%s[%d]", str.c_str(), id);
+ int left = id2ast->children[1]->children[0]->integer;
+ int right = id2ast->children[1]->children[1]->integer;
+ bool valid_const_access =
+ (left <= id && id <= right) ||
+ (right <= id && id <= left);
+ if (valid_const_access)
+ {
+ str = stringf("%s[%d]", str.c_str(), id);
+ delete_children();
+ range_valid = false;
+ id2ast = NULL;
+ }
+ else
+ {
+ int width;
+ if (bit_part_sel)
+ {
+ bit_part_sel->dumpAst(nullptr, "? ");
+ if (bit_part_sel->children.size() == 1)
+ width = 0;
+ else
+ width = bit_part_sel->children[0]->integer -
+ bit_part_sel->children[1]->integer;
+ delete bit_part_sel;
+ bit_part_sel = nullptr;
+ }
+ else
+ {
+ width = id2ast->children[0]->children[0]->integer -
+ id2ast->children[0]->children[1]->integer;
+ }
+ width = abs(width) + 1;
- delete_children();
- range_valid = false;
- id2ast = NULL;
+ delete_children();
+
+ std::vector<RTLIL::State> x_bits;
+ for (int i = 0; i < width; i++)
+ x_bits.push_back(RTLIL::State::Sx);
+ AstNode *constant = AstNode::mkconst_bits(x_bits, false);
+ constant->cloneInto(this);
+ delete constant;
+ }
}
else
{
@@ -4663,6 +5153,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
{
std::map<std::string, AstNode*> backup_scope = current_scope;
std::map<std::string, AstNode::varinfo_t> variables;
+ std::vector<AstNode*> to_delete;
AstNode *block = new AstNode(AST_BLOCK);
AstNode *result = nullptr;
@@ -4720,6 +5211,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
current_scope[stmt->str] = stmt;
block->children.erase(block->children.begin());
+ to_delete.push_back(stmt);
continue;
}
@@ -4732,6 +5224,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
current_scope[stmt->str] = stmt;
block->children.erase(block->children.begin());
+ to_delete.push_back(stmt);
continue;
}
@@ -4927,12 +5420,20 @@ finished:
delete block;
current_scope = backup_scope;
+ for (auto it : to_delete) {
+ delete it;
+ }
+ to_delete.clear();
+
return result;
}
void AstNode::allocateDefaultEnumValues()
{
log_assert(type==AST_ENUM);
+ log_assert(children.size() > 0);
+ if (children.front()->attributes.count(ID::enum_base_type))
+ return; // already elaborated
int last_enum_int = -1;
for (auto node : children) {
log_assert(node->type==AST_ENUM_ITEM);
@@ -5008,4 +5509,21 @@ std::pair<AstNode*, AstNode*> AstNode::get_tern_choice()
return {choice, not_choice};
}
+std::string AstNode::try_pop_module_prefix() const
+{
+ AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
+ size_t pos = str.find('.', 1);
+ if (str[0] == '\\' && pos != std::string::npos) {
+ std::string new_str = "\\" + str.substr(pos + 1);
+ if (current_scope.count(new_str)) {
+ std::string prefix = str.substr(0, pos);
+ auto it = current_scope_ast->attributes.find(ID::hdlname);
+ if ((it != current_scope_ast->attributes.end() && it->second->str == prefix)
+ || prefix == current_scope_ast->str)
+ return new_str;
+ }
+ }
+ return str;
+}
+
YOSYS_NAMESPACE_END
diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc
index 9ae3fac2c..19844bda6 100644
--- a/frontends/blif/blifparse.cc
+++ b/frontends/blif/blifparse.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -65,17 +65,21 @@ static std::pair<RTLIL::IdString, int> wideports_split(std::string name)
for (int i = 0; i+1 < GetSize(name); i++) {
if (name[i] == '[')
pos = i;
- else if (name[i] < '0' || name[i] > '9')
+ else if (name[i] != '-' && (name[i] < '0' || name[i] > '9'))
+ pos = -1;
+ else if (name[i] == '-' && ((i != pos+1) || name[i+1] == ']'))
+ pos = -1;
+ else if (i == pos+2 && name[i] == '0' && name[i-1] == '-')
pos = -1;
else if (i == pos+1 && name[i] == '0' && name[i+1] != ']')
pos = -1;
}
if (pos >= 0)
- return std::pair<RTLIL::IdString, int>("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)+1);
+ return std::pair<RTLIL::IdString, int>("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1));
failed:
- return std::pair<RTLIL::IdString, int>("\\" + name, 0);
+ return std::pair<RTLIL::IdString, int>(RTLIL::IdString(), 0);
}
void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool run_clean, bool sop_mode, bool wideports)
@@ -263,8 +267,8 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
if (wideports) {
std::pair<RTLIL::IdString, int> wp = wideports_split(p);
- if (wp.second > 0) {
- wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second);
+ if (!wp.first.empty() && wp.second >= 0) {
+ wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second + 1);
wideports_cache[wp.first].second = !strcmp(cmd, ".inputs");
}
}
@@ -375,6 +379,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
IdString celltype = RTLIL::escape_id(p);
RTLIL::Cell *cell = module->addCell(NEW_ID, celltype);
+ RTLIL::Module *cell_mod = design->module(celltype);
dict<RTLIL::IdString, dict<int, SigBit>> cell_wideports_cache;
@@ -387,10 +392,10 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
if (wideports) {
std::pair<RTLIL::IdString, int> wp = wideports_split(p);
- if (wp.second > 0)
- cell_wideports_cache[wp.first][wp.second-1] = blif_wire(q);
- else
+ if (wp.first.empty())
cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
+ else
+ cell_wideports_cache[wp.first][wp.second] = blif_wire(q);
} else {
cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
}
@@ -399,14 +404,26 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
for (auto &it : cell_wideports_cache)
{
int width = 0;
+ int offset = 0;
+ bool upto = false;
for (auto &b : it.second)
width = std::max(width, b.first + 1);
+ if (cell_mod) {
+ Wire *cell_port = cell_mod->wire(it.first);
+ if (cell_port && (cell_port->port_input || cell_port->port_output)) {
+ offset = cell_port->start_offset;
+ upto = cell_port->upto;
+ width = cell_port->width;
+ }
+ }
+
SigSpec sig;
for (int i = 0; i < width; i++) {
- if (it.second.count(i))
- sig.append(it.second.at(i));
+ int idx = offset + (upto ? width - 1 - i: i);
+ if (it.second.count(idx))
+ sig.append(it.second.at(idx));
else
sig.append(module->addWire(NEW_ID));
}
diff --git a/frontends/blif/blifparse.h b/frontends/blif/blifparse.h
index 2b84cb795..d7a3c96b1 100644
--- a/frontends/blif/blifparse.h
+++ b/frontends/blif/blifparse.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc
index 312f6d3be..50c25abda 100644
--- a/frontends/json/jsonparse.cc
+++ b/frontends/json/jsonparse.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -539,6 +539,56 @@ void json_import(Design *design, string &modname, JsonNode *node)
json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters"));
}
}
+
+ if (node->data_dict.count("memories"))
+ {
+ JsonNode *memories_node = node->data_dict.at("memories");
+
+ if (memories_node->type != 'D')
+ log_error("JSON memories node is not a dictionary.\n");
+
+ for (auto &memory_node_it : memories_node->data_dict)
+ {
+ IdString memory_name = RTLIL::escape_id(memory_node_it.first.c_str());
+ JsonNode *memory_node = memory_node_it.second;
+
+ RTLIL::Memory *mem = new RTLIL::Memory;
+ mem->name = memory_name;
+
+ if (memory_node->type != 'D')
+ log_error("JSON memory node '%s' is not a dictionary.\n", log_id(memory_name));
+
+ if (memory_node->data_dict.count("width") == 0)
+ log_error("JSON memory node '%s' has no width attribute.\n", log_id(memory_name));
+ JsonNode *width_node = memory_node->data_dict.at("width");
+ if (width_node->type != 'N')
+ log_error("JSON memory node '%s' has a non-number width.\n", log_id(memory_name));
+ mem->width = width_node->data_number;
+
+ if (memory_node->data_dict.count("size") == 0)
+ log_error("JSON memory node '%s' has no size attribute.\n", log_id(memory_name));
+ JsonNode *size_node = memory_node->data_dict.at("size");
+ if (size_node->type != 'N')
+ log_error("JSON memory node '%s' has a non-number size.\n", log_id(memory_name));
+ mem->size = size_node->data_number;
+
+ mem->start_offset = 0;
+ if (memory_node->data_dict.count("start_offset") != 0) {
+ JsonNode *val = memory_node->data_dict.at("start_offset");
+ if (val->type == 'N')
+ mem->start_offset = val->data_number;
+ }
+
+ if (memory_node->data_dict.count("attributes"))
+ json_parse_attr_param(mem->attributes, memory_node->data_dict.at("attributes"));
+
+ module->memories[mem->name] = mem;
+ }
+ }
+
+ // remove duplicates from connections array
+ pool<RTLIL::SigSig> unique_connections(module->connections_.begin(), module->connections_.end());
+ module->connections_ = std::vector<RTLIL::SigSig>(unique_connections.begin(), unique_connections.end());
}
struct JsonFrontend : public Frontend {
diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc
index f77d7da56..abf8de4d1 100644
--- a/frontends/liberty/liberty.cc
+++ b/frontends/liberty/liberty.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/frontends/rtlil/rtlil_frontend.cc b/frontends/rtlil/rtlil_frontend.cc
index 00c34175e..5f85ca2b8 100644
--- a/frontends/rtlil/rtlil_frontend.cc
+++ b/frontends/rtlil/rtlil_frontend.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/frontends/rtlil/rtlil_frontend.h b/frontends/rtlil/rtlil_frontend.h
index a420778b0..189260605 100644
--- a/frontends/rtlil/rtlil_frontend.h
+++ b/frontends/rtlil/rtlil_frontend.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/frontends/rtlil/rtlil_lexer.l b/frontends/rtlil/rtlil_lexer.l
index beef220f6..e16413216 100644
--- a/frontends/rtlil/rtlil_lexer.l
+++ b/frontends/rtlil/rtlil_lexer.l
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -79,6 +79,7 @@ USING_YOSYS_NAMESPACE
"global" { return TOK_GLOBAL; }
"init" { return TOK_INIT; }
"update" { return TOK_UPDATE; }
+"memwr" { return TOK_MEMWR; }
"process" { return TOK_PROCESS; }
"end" { return TOK_END; }
diff --git a/frontends/rtlil/rtlil_parser.y b/frontends/rtlil/rtlil_parser.y
index 646489196..7d99b2c42 100644
--- a/frontends/rtlil/rtlil_parser.y
+++ b/frontends/rtlil/rtlil_parser.y
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -22,6 +22,8 @@
*
*/
+%require "3.0"
+
%{
#include <list>
#include "frontends/rtlil/rtlil_frontend.h"
@@ -69,7 +71,7 @@ USING_YOSYS_NAMESPACE
%token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT
-%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
+%token TOK_UPDATE TOK_MEMWR TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO
%type <rsigspec> sigspec_list_reversed
@@ -155,6 +157,7 @@ param_defval_stmt:
TOK_PARAMETER TOK_ID constant EOL {
current_module->avail_parameters($2);
current_module->parameter_default_values[$2] = *$3;
+ delete $3;
free($2);
};
@@ -282,10 +285,8 @@ proc_stmt:
TOK_PROCESS TOK_ID EOL {
if (current_module->processes.count($2) != 0)
rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of process %s.", $2).c_str());
- current_process = new RTLIL::Process;
- current_process->name = $2;
+ current_process = current_module->addProcess($2);
current_process->attributes = attrbuf;
- current_module->processes[$2] = current_process;
switch_stack.clear();
switch_stack.push_back(&current_process->root_case.switches);
case_stack.clear();
@@ -389,6 +390,22 @@ update_list:
delete $3;
delete $4;
} |
+ update_list attr_list TOK_MEMWR TOK_ID sigspec sigspec sigspec constant EOL {
+ RTLIL::MemWriteAction act;
+ act.attributes = attrbuf;
+ act.memid = $4;
+ act.address = *$5;
+ act.data = *$6;
+ act.enable = *$7;
+ act.priority_mask = *$8;
+ current_process->syncs.back()->mem_write_actions.push_back(std::move(act));
+ attrbuf.clear();
+ free($4);
+ delete $5;
+ delete $6;
+ delete $7;
+ delete $8;
+ } |
/* empty */;
constant:
diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc
index 972f4f9f1..c82428613 100644
--- a/frontends/verific/Makefile.inc
+++ b/frontends/verific/Makefile.inc
@@ -10,9 +10,11 @@ EXTRA_TARGETS += share/verific
share/verific:
$(P) rm -rf share/verific.new
$(Q) mkdir -p share/verific.new
+ifneq ($(DISABLE_VERIFIC_VHDL),1)
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008
+endif
$(Q) chmod -R a+rX share/verific.new
$(Q) mv share/verific.new share/verific
diff --git a/frontends/verific/README b/frontends/verific/README
index c37d76343..952fb1e0c 100644
--- a/frontends/verific/README
+++ b/frontends/verific/README
@@ -1,11 +1,11 @@
This directory contains Verific bindings for Yosys.
-Use Symbiotic EDA Suite if you need Yosys+Verifc.
-https://www.symbioticeda.com/seda-suite
+Use Tabby CAD Suite from YosysHQ if you need Yosys+Verifc.
+https://www.yosyshq.com/
-Contact office@symbioticeda.com for free evaluation
-binaries of Symbiotic EDA Suite.
+Contact YosysHQ at contact@yosyshq.com for free evaluation
+binaries of Tabby CAD Suite.
Verific Features that should be enabled in your Verific library
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index 7aa3ebcbb..17dbed067 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -43,22 +43,25 @@ USING_YOSYS_NAMESPACE
#endif
#include "veri_file.h"
-#include "vhdl_file.h"
#include "hier_tree.h"
#include "VeriModule.h"
#include "VeriWrite.h"
-#include "VhdlUnits.h"
#include "VeriLibrary.h"
-#if defined(YOSYSHQ_VERIFIC_INITSTATE) || defined(YOSYSHQ_VERIFIC_TEMPLATES) || defined(YOSYSHQ_VERIFIC_FORMALAPPS)
-#include "VeriExtensions.h"
+#ifdef VERIFIC_VHDL_SUPPORT
+#include "vhdl_file.h"
+#include "VhdlUnits.h"
+#endif
+
+#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
+#include "InitialAssertions.h"
#endif
#ifndef YOSYSHQ_VERIFIC_API_VERSION
# error "Only YosysHQ flavored Verific is supported. Please contact office@yosyshq.com for commercial support for Yosys+Verific."
#endif
-#if YOSYSHQ_VERIFIC_API_VERSION < 20210103
+#if YOSYSHQ_VERIFIC_API_VERSION < 20210801
# error "Please update your version of YosysHQ flavored Verific."
#endif
@@ -175,8 +178,10 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
return;
if (!type_range->IsTypeEnum())
return;
+#ifdef VERIFIC_VHDL_SUPPORT
if (nl->IsFromVhdl() && strcmp(type_range->GetTypeName(), "STD_LOGIC") == 0)
return;
+#endif
auto type_name = type_range->GetTypeName();
if (!type_name)
return;
@@ -193,7 +198,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
p = nullptr;
else
for (auto q = p+2; *q != '\0'; q++)
- if (*q != '0' && *q != '1') {
+ if (*q != '0' && *q != '1' && *q != 'x' && *q != 'z') {
p = nullptr;
break;
}
@@ -202,6 +207,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
log_error("Expected TypeRange value '%s' to be of form <decimal>'b<binary>.\n", v);
attributes.emplace(stringf("\\enum_value_%s", p+2), RTLIL::escape_id(k));
}
+#ifdef VERIFIC_VHDL_SUPPORT
else if (nl->IsFromVhdl()) {
// Expect "<binary>" or plain <binary>
auto p = v;
@@ -237,6 +243,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
if (p == nullptr)
log_error("Expected TypeRange value '%s' to be of form \"<binary>\" or <binary>.\n", v);
}
+#endif
}
}
}
@@ -371,7 +378,7 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr
return true;
}
- if (inst->Type() == PRIM_TRI) {
+ if ((inst->Type() == PRIM_TRI) || (inst->Type() == PRIM_BUFIF1)) {
module->addMuxGate(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
return true;
}
@@ -410,6 +417,42 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr
return true;
}
+ if (inst->Type() == PRIM_DLATCHRS)
+ {
+ if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
+ module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ else
+ module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()),
+ net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DFF)
+ {
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ if (inst->GetAsyncCond()->IsGnd())
+ clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ else
+ clocking.addAldff(inst_name, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()),
+ net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DLATCH)
+ {
+ if (inst->GetAsyncCond()->IsGnd()) {
+ module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ } else {
+ RTLIL::SigSpec sig_set = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()));
+ RTLIL::SigSpec sig_clr = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), module->Not(NEW_ID, net_map_at(inst->GetAsyncVal())));
+ module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_clr, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ }
+ return true;
+ }
+
return false;
}
@@ -471,7 +514,7 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
return true;
}
- if (inst->Type() == PRIM_TRI) {
+ if ((inst->Type() == PRIM_TRI) || (inst->Type() == PRIM_BUFIF1)) {
cell = module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
import_attributes(cell->attributes, inst);
return true;
@@ -520,6 +563,34 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
return true;
}
+ if (inst->Type() == PRIM_DFF)
+ {
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ if (inst->GetAsyncCond()->IsGnd())
+ cell = clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ else
+ cell = clocking.addAldff(inst_name, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()),
+ net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DLATCH)
+ {
+ if (inst->GetAsyncCond()->IsGnd()) {
+ cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ } else {
+ RTLIL::SigSpec sig_set = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()));
+ RTLIL::SigSpec sig_clr = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), module->Not(NEW_ID, net_map_at(inst->GetAsyncVal())));
+ cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_clr, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ }
+ import_attributes(cell->attributes, inst);
+ return true;
+ }
+
#define IN operatorInput(inst)
#define IN1 operatorInput1(inst)
#define IN2 operatorInput2(inst)
@@ -727,28 +798,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
}
if (inst->Type() == OPER_NTO1MUX) {
- cell = module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput()));
+ cell = module->addBmux(inst_name, IN2, IN1, net_map_at(inst->GetOutput()));
import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_NTO1MUX)
{
- SigSpec data = IN2, out = OUT;
-
- int wordsize_bits = ceil_log2(GetSize(out));
- int wordsize = 1 << wordsize_bits;
-
- SigSpec sel = {IN1, SigSpec(State::S0, wordsize_bits)};
-
- SigSpec padded_data;
- for (int i = 0; i < GetSize(data); i += GetSize(out)) {
- SigSpec d = data.extract(i, GetSize(out));
- d.extend_u0(wordsize);
- padded_data.append(d);
- }
-
- cell = module->addShr(inst_name, padded_data, sel, out);
+ cell = module->addBmux(inst_name, IN2, IN1, OUT);
import_attributes(cell->attributes, inst);
return true;
}
@@ -792,6 +849,74 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
return true;
}
+ if (inst->Type() == OPER_WIDE_DLATCHRS)
+ {
+ RTLIL::SigSpec sig_set = operatorInport(inst, "set");
+ RTLIL::SigSpec sig_reset = operatorInport(inst, "reset");
+
+ if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool())
+ cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), IN, OUT);
+ else
+ cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_reset, IN, OUT);
+ import_attributes(cell->attributes, inst);
+
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_DFF)
+ {
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ RTLIL::SigSpec sig_d = IN;
+ RTLIL::SigSpec sig_q = OUT;
+ RTLIL::SigSpec sig_adata = IN1;
+ RTLIL::SigSpec sig_acond = IN2;
+
+ if (sig_acond.is_fully_const() && !sig_acond.as_bool()) {
+ cell = clocking.addDff(inst_name, sig_d, sig_q);
+ import_attributes(cell->attributes, inst);
+ } else {
+ int offset = 0, width = 0;
+ for (offset = 0; offset < GetSize(sig_acond); offset += width) {
+ for (width = 1; offset+width < GetSize(sig_acond); width++)
+ if (sig_acond[offset] != sig_acond[offset+width]) break;
+ cell = clocking.addAldff(module->uniquify(inst_name), sig_acond[offset], sig_adata.extract(offset, width),
+ sig_d.extract(offset, width), sig_q.extract(offset, width));
+ import_attributes(cell->attributes, inst);
+ }
+ }
+
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_DLATCH)
+ {
+ RTLIL::SigSpec sig_d = IN;
+ RTLIL::SigSpec sig_q = OUT;
+ RTLIL::SigSpec sig_adata = IN1;
+ RTLIL::SigSpec sig_acond = IN2;
+
+ if (sig_acond.is_fully_const() && !sig_acond.as_bool()) {
+ cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), sig_d, sig_q);
+ import_attributes(cell->attributes, inst);
+ } else {
+ int offset = 0, width = 0;
+ for (offset = 0; offset < GetSize(sig_acond); offset += width) {
+ for (width = 1; offset+width < GetSize(sig_acond); width++)
+ if (sig_acond[offset] != sig_acond[offset+width]) break;
+ RTLIL::SigSpec sig_set = module->Mux(NEW_ID, RTLIL::SigSpec(0, width), sig_adata.extract(offset, width), sig_acond[offset]);
+ RTLIL::SigSpec sig_clr = module->Mux(NEW_ID, RTLIL::SigSpec(0, width), module->Not(NEW_ID, sig_adata.extract(offset, width)), sig_acond[offset]);
+ cell = module->addDlatchsr(module->uniquify(inst_name), net_map_at(inst->GetControl()), sig_set, sig_clr,
+ sig_d.extract(offset, width), sig_q.extract(offset, width));
+ import_attributes(cell->attributes, inst);
+ }
+ }
+
+ return true;
+ }
+
#undef IN
#undef IN1
#undef IN2
@@ -917,6 +1042,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
} else {
log("Importing module %s.\n", RTLIL::id2cstr(module->name));
}
+ import_attributes(module->attributes, nl, nl);
SetIter si;
MapIter mi, mi2;
@@ -965,18 +1091,28 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex());
import_attributes(wire->attributes, portbus, nl);
- if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN)
+ bool portbus_input = portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN;
+ if (portbus_input)
wire->port_input = true;
if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_OUT)
wire->port_output = true;
for (int i = portbus->LeftIndex();; i += portbus->IsUp() ? +1 : -1) {
if (portbus->ElementAtIndex(i) && portbus->ElementAtIndex(i)->GetNet()) {
+ bool bit_input = portbus_input;
+ if (portbus->GetDir() == DIR_NONE) {
+ Port *p = portbus->ElementAtIndex(i);
+ bit_input = p->GetDir() == DIR_INOUT || p->GetDir() == DIR_IN;
+ if (bit_input)
+ wire->port_input = true;
+ if (p->GetDir() == DIR_INOUT || p->GetDir() == DIR_OUT)
+ wire->port_output = true;
+ }
net = portbus->ElementAtIndex(i)->GetNet();
RTLIL::SigBit bit(wire, i - wire->start_offset);
if (net_map.count(net) == 0)
net_map[net] = bit;
- else if (wire->port_input)
+ else if (bit_input)
module->connect(net_map_at(net), bit);
else
module->connect(bit, net_map_at(net));
@@ -1003,7 +1139,6 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
module->memories[memory->name] = memory;
int number_of_bits = net->Size();
- number_of_bits = 1 << ceil_log2(number_of_bits);
int bits_in_word = number_of_bits;
FOREACH_PORTREF_OF_NET(net, si, pr) {
if (pr->GetInst()->Type() == OPER_READ_PORT) {
@@ -1474,9 +1609,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
continue;
}
-#ifdef YOSYSHQ_VERIFIC_INITSTATE
if (inst->Type() == PRIM_YOSYSHQ_INITSTATE)
{
+ if (verific_verbose)
+ log(" adding YosysHQ init state\n");
SigBit initstate = module->Initstate(new_verific_id(inst));
SigBit sig_o = net_map_at(inst->GetOutput());
module->connect(sig_o, initstate);
@@ -1484,7 +1620,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
if (!mode_keep)
continue;
}
-#endif
+
if (!mode_keep && verific_sva_prims.count(inst->Type())) {
if (verific_verbose)
log(" skipping SVA cell in non k-mode\n");
@@ -1756,30 +1892,62 @@ Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const
{
log_assert(GetSize(sig_d) == GetSize(sig_q));
- if (GetSize(init_value) != 0) {
- log_assert(GetSize(sig_q) == GetSize(init_value));
- if (sig_q.is_wire()) {
- sig_q.as_wire()->attributes[ID::init] = init_value;
+ auto set_init_attribute = [&](SigSpec &s) {
+ if (GetSize(init_value) == 0)
+ return;
+ log_assert(GetSize(s) == GetSize(init_value));
+ if (s.is_wire()) {
+ s.as_wire()->attributes[ID::init] = init_value;
} else {
- Wire *w = module->addWire(NEW_ID, GetSize(sig_q));
+ Wire *w = module->addWire(NEW_ID, GetSize(s));
w->attributes[ID::init] = init_value;
- module->connect(sig_q, w);
- sig_q = w;
+ module->connect(s, w);
+ s = w;
}
- }
+ };
if (enable_sig != State::S1)
sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
if (disable_sig != State::S0) {
- log_assert(gclk == false);
log_assert(GetSize(sig_q) == GetSize(init_value));
+
+ if (gclk) {
+ Wire *pre_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *post_q_w = module->addWire(NEW_ID, GetSize(sig_q));
+
+ Const initval(State::Sx, GetSize(sig_q));
+ int offset = 0;
+ for (auto c : sig_q.chunks()) {
+ if (c.wire && c.wire->attributes.count(ID::init)) {
+ Const val = c.wire->attributes.at(ID::init);
+ for (int i = 0; i < GetSize(c); i++)
+ initval[offset+i] = val[c.offset+i];
+ }
+ offset += GetSize(c);
+ }
+
+ if (!initval.is_fully_undef())
+ post_q_w->attributes[ID::init] = initval;
+
+ module->addMux(NEW_ID, sig_d, init_value, disable_sig, pre_d);
+ module->addMux(NEW_ID, post_q_w, init_value, disable_sig, sig_q);
+
+ SigSpec post_q(post_q_w);
+ set_init_attribute(post_q);
+ return module->addFf(name, pre_d, post_q);
+ }
+
+ set_init_attribute(sig_q);
return module->addAdff(name, clock_sig, disable_sig, sig_d, sig_q, init_value, posedge);
}
- if (gclk)
+ if (gclk) {
+ set_init_attribute(sig_q);
return module->addFf(name, sig_d, sig_q);
+ }
+ set_init_attribute(sig_q);
return module->addDff(name, clock_sig, sig_d, sig_q, posedge);
}
@@ -1788,6 +1956,7 @@ Cell *VerificClocking::addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec s
log_assert(gclk == false);
log_assert(disable_sig == State::S0);
+ // FIXME: Adffe
if (enable_sig != State::S1)
sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
@@ -1799,12 +1968,48 @@ Cell *VerificClocking::addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::Si
log_assert(gclk == false);
log_assert(disable_sig == State::S0);
+ // FIXME: Dffsre
if (enable_sig != State::S1)
sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
return module->addDffsr(name, clock_sig, sig_set, sig_clr, sig_d, sig_q, posedge);
}
+Cell *VerificClocking::addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::SigSpec sig_adata, SigSpec sig_d, SigSpec sig_q)
+{
+ log_assert(disable_sig == State::S0);
+
+ // FIXME: Aldffe
+ if (enable_sig != State::S1)
+ sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
+
+ if (gclk) {
+ Wire *pre_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *post_q = module->addWire(NEW_ID, GetSize(sig_q));
+
+ Const initval(State::Sx, GetSize(sig_q));
+ int offset = 0;
+ for (auto c : sig_q.chunks()) {
+ if (c.wire && c.wire->attributes.count(ID::init)) {
+ Const val = c.wire->attributes.at(ID::init);
+ for (int i = 0; i < GetSize(c); i++)
+ initval[offset+i] = val[c.offset+i];
+ }
+ offset += GetSize(c);
+ }
+
+ if (!initval.is_fully_undef())
+ post_q->attributes[ID::init] = initval;
+
+ module->addMux(NEW_ID, sig_d, sig_adata, sig_aload, pre_d);
+ module->addMux(NEW_ID, post_q, sig_adata, sig_aload, sig_q);
+
+ return module->addFf(name, pre_d, post_q);
+ }
+
+ return module->addAldff(name, clock_sig, sig_aload, sig_d, sig_q, sig_adata, posedge);
+}
+
// ==================================================================
struct VerificExtNets
@@ -1951,21 +2156,23 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
std::set<Netlist*> nl_todo, nl_done;
- VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1);
Array *netlists = NULL;
Array veri_libs, vhdl_libs;
+#ifdef VERIFIC_VHDL_SUPPORT
+ VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
+#endif
if (veri_lib) veri_libs.InsertLast(veri_lib);
Map verific_params(STRING_HASH);
for (const auto &i : parameters)
verific_params.Insert(i.first.c_str(), i.second.c_str());
-#ifdef YOSYSHQ_VERIFIC_INITSTATE
- InitialAssertionRewriter rw;
- rw.RegisterCallBack();
+#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
+ InitialAssertions::Rewrite("work", &verific_params);
#endif
+
if (top.empty()) {
netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params);
}
@@ -1986,12 +2193,13 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
}
}
+#ifdef VERIFIC_VHDL_SUPPORT
if (vhdl_lib) {
VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str());
if (vhdl_unit)
vhdl_units.InsertLast(vhdl_unit);
}
-
+#endif
netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params);
}
@@ -2028,7 +2236,9 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
}
veri_file::Reset();
+#ifdef VERIFIC_VHDL_SUPPORT
vhdl_file::Reset();
+#endif
Libset::Reset();
verific_incdirs.clear();
verific_libdirs.clear();
@@ -2071,7 +2281,7 @@ struct VerificPass : public Pass {
log("\n");
log("Additional -D<macro>[=<value>] options may be added after the option indicating\n");
log("the language version (and before file names) to set additional verilog defines.\n");
- log("The macros SYNTHESIS and VERIFIC are defined implicitly.\n");
+ log("The macros YOSYS, SYNTHESIS, and VERIFIC are defined implicitly.\n");
log("\n");
log("\n");
log(" verific -formal <verilog-file>..\n");
@@ -2079,11 +2289,41 @@ struct VerificPass : public Pass {
log("Like -sv, but define FORMAL instead of SYNTHESIS.\n");
log("\n");
log("\n");
+#ifdef VERIFIC_VHDL_SUPPORT
log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} <vhdl-file>..\n");
log("\n");
log("Load the specified VHDL files into Verific.\n");
log("\n");
log("\n");
+#endif
+ log(" verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv|-formal] <command-file>\n");
+ log("\n");
+ log("Load and execute the specified command file.\n");
+ log("Override verilog parsing mode can be set.\n");
+ log("The macros YOSYS, SYNTHESIS/FORMAL, and VERIFIC are defined implicitly.\n");
+ log("\n");
+ log("Command file parser supports following commands:\n");
+ log(" +define - defines macro\n");
+ log(" -u - upper case all identifier (makes Verilog parser case insensitive)\n");
+ log(" -v - register library name (file)\n");
+ log(" -y - register library name (directory)\n");
+ log(" +incdir - specify include dir\n");
+ log(" +libext - specify library extension\n");
+ log(" +liborder - add library in ordered list\n");
+ log(" +librescan - unresolved modules will be always searched starting with the first\n");
+ log(" library specified by -y/-v options.\n");
+ log(" -f/-file - nested -f option\n");
+ log(" -F - nested -F option\n");
+ log("\n");
+ log(" parse mode:\n");
+ log(" -ams\n");
+ log(" +systemverilogext\n");
+ log(" +v2k\n");
+ log(" +verilog1995ext\n");
+ log(" +verilog2001ext\n");
+ log(" -sverilog\n");
+ log("\n");
+ log("\n");
log(" verific [-work <libname>] {-sv|-vhdl|...} <hdl-file>\n");
log("\n");
log("Load the specified Verilog/SystemVerilog/VHDL file into the specified library.\n");
@@ -2255,6 +2495,13 @@ struct VerificPass : public Pass {
log(" WARNING: Templates only available in commercial build.\n");
log("\n");
#endif
+ log("\n");
+ log("\n");
+ log(" verific -cfg [<name> [<value>]]\n");
+ log("\n");
+ log("Get/set Verific runtime flags.\n");
+ log("\n");
+ log("\n");
log("Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n");
log("https://www.yosyshq.com/\n");
log("\n");
@@ -2283,24 +2530,31 @@ struct VerificPass : public Pass {
Message::SetConsoleOutput(0);
Message::RegisterCallBackMsg(msg_func);
+ RuntimeFlags::SetVar("db_preserve_user_instances", 1);
RuntimeFlags::SetVar("db_preserve_user_nets", 1);
+ RuntimeFlags::SetVar("db_preserve_x", 1);
+
RuntimeFlags::SetVar("db_allow_external_nets", 1);
RuntimeFlags::SetVar("db_infer_wide_operators", 1);
+ RuntimeFlags::SetVar("db_infer_set_reset_registers", 0);
RuntimeFlags::SetVar("veri_extract_dualport_rams", 0);
RuntimeFlags::SetVar("veri_extract_multiport_rams", 1);
+#ifdef VERIFIC_VHDL_SUPPORT
RuntimeFlags::SetVar("vhdl_extract_dualport_rams", 0);
RuntimeFlags::SetVar("vhdl_extract_multiport_rams", 1);
RuntimeFlags::SetVar("vhdl_support_variable_slice", 1);
RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0);
- RuntimeFlags::SetVar("veri_preserve_assignments", 1);
RuntimeFlags::SetVar("vhdl_preserve_assignments", 1);
-
- RuntimeFlags::SetVar("veri_preserve_comments",1);
- //RuntimeFlags::SetVar("vhdl_preserve_comments",1);
+ //RuntimeFlags::SetVar("vhdl_preserve_comments", 1);
+ RuntimeFlags::SetVar("vhdl_preserve_drivers", 1);
+#endif
+ RuntimeFlags::SetVar("veri_preserve_assignments", 1);
+ RuntimeFlags::SetVar("veri_preserve_comments", 1);
+ RuntimeFlags::SetVar("veri_preserve_drivers", 1);
// Workaround for VIPER #13851
RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1);
@@ -2311,6 +2565,8 @@ struct VerificPass : public Pass {
// https://github.com/YosysHQ/yosys/issues/1055
RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ;
+ RuntimeFlags::SetVar("verific_produce_verbose_syntax_error_message", 1);
+
#ifndef DB_PRESERVE_INITIAL_VALUE
# warning Verific was built without DB_PRESERVE_INITIAL_VALUE.
#endif
@@ -2407,6 +2663,65 @@ struct VerificPass : public Pass {
break;
}
+ if (GetSize(args) > argidx && (args[argidx] == "-f" || args[argidx] == "-F"))
+ {
+ unsigned verilog_mode = veri_file::VERILOG_95; // default recommended by Verific
+ bool is_formal = false;
+ const char* filename = nullptr;
+
+ Verific::veri_file::f_file_flags flags = (args[argidx] == "-f") ? veri_file::F_FILE_NONE : veri_file::F_FILE_CAPITAL;
+
+ for (argidx++; argidx < GetSize(args); argidx++) {
+ if (args[argidx] == "-vlog95") {
+ verilog_mode = veri_file::VERILOG_95;
+ continue;
+ } else if (args[argidx] == "-vlog2k") {
+ verilog_mode = veri_file::VERILOG_2K;
+ continue;
+ } else if (args[argidx] == "-sv2005") {
+ verilog_mode = veri_file::SYSTEM_VERILOG_2005;
+ continue;
+ } else if (args[argidx] == "-sv2009") {
+ verilog_mode = veri_file::SYSTEM_VERILOG_2009;
+ continue;
+ } else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal") {
+ verilog_mode = veri_file::SYSTEM_VERILOG;
+ if (args[argidx] == "-formal") is_formal = true;
+ continue;
+ } else if (args[argidx].compare(0, 1, "-") == 0) {
+ cmd_error(args, argidx, "unknown option");
+ goto check_error;
+ }
+
+ if (!filename) {
+ filename = args[argidx].c_str();
+ continue;
+ } else {
+ log_cmd_error("Only one filename can be specified.\n");
+ }
+ }
+ if (!filename)
+ log_cmd_error("Filname must be specified.\n");
+
+ unsigned analysis_mode = verilog_mode; // keep default as provided by user if not defined in file
+ Array *file_names = veri_file::ProcessFFile(filename, flags, analysis_mode);
+ if (analysis_mode != verilog_mode)
+ log_warning("Provided verilog mode differs from one specified in file.\n");
+
+ veri_file::DefineMacro("YOSYS");
+ veri_file::DefineMacro("VERIFIC");
+ veri_file::DefineMacro(is_formal ? "FORMAL" : "SYNTHESIS");
+
+ if (!veri_file::AnalyzeMultipleFiles(file_names, verilog_mode, work.c_str(), veri_file::MFCU)) {
+ verific_error_msg.clear();
+ log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n");
+ }
+
+ delete file_names;
+ verific_import_pending = true;
+ goto check_error;
+ }
+
if (GetSize(args) > argidx && (args[argidx] == "-vlog95" || args[argidx] == "-vlog2k" || args[argidx] == "-sv2005" ||
args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal"))
{
@@ -2426,6 +2741,7 @@ struct VerificPass : public Pass {
else
log_abort();
+ veri_file::DefineMacro("YOSYS");
veri_file::DefineMacro("VERIFIC");
veri_file::DefineMacro(args[argidx] == "-formal" ? "FORMAL" : "SYNTHESIS");
@@ -2463,6 +2779,7 @@ struct VerificPass : public Pass {
goto check_error;
}
+#ifdef VERIFIC_VHDL_SUPPORT
if (GetSize(args) > argidx && args[argidx] == "-vhdl87") {
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str());
for (argidx++; argidx < GetSize(args); argidx++)
@@ -2498,6 +2815,7 @@ struct VerificPass : public Pass {
verific_import_pending = true;
goto check_error;
}
+#endif
#ifdef YOSYSHQ_VERIFIC_FORMALAPPS
if (argidx < GetSize(args) && args[argidx] == "-app")
@@ -2600,10 +2918,12 @@ struct VerificPass : public Pass {
const char* module = nullptr;
bool mode_vhdl = false;
for (argidx++; argidx < GetSize(args); argidx++) {
+#ifdef VERIFIC_VHDL_SUPPORT
if (args[argidx] == "-vhdl") {
mode_vhdl = true;
continue;
}
+#endif
if (args[argidx] == "-verilog") {
mode_vhdl = false;
continue;
@@ -2630,7 +2950,11 @@ struct VerificPass : public Pass {
log_cmd_error("Filname must be specified.\n");
if (mode_vhdl)
+#ifdef VERIFIC_VHDL_SUPPORT
vhdl_file::PrettyPrint(filename, module, work.c_str());
+#else
+ goto check_error;
+#endif
else
veri_file::PrettyPrint(filename, module, work.c_str());
goto check_error;
@@ -2651,7 +2975,7 @@ struct VerificPass : public Pass {
if (!(argidx+1 < GetSize(args)))
cmd_error(args, argidx+1, "No top module specified.\n");
generator->setLogger([](std::string msg) { log("%s",msg.c_str()); } );
-
+
std::string module = args[++argidx];
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
VeriModule *veri_module = veri_lib ? veri_lib->GetModule(module.c_str(), 1) : nullptr;
@@ -2805,19 +3129,20 @@ struct VerificPass : public Pass {
std::set<std::string> top_mod_names;
-#ifdef YOSYSHQ_VERIFIC_INITSTATE
- InitialAssertionRewriter rw;
- rw.RegisterCallBack();
+#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
+ InitialAssertions::Rewrite(work, &parameters);
#endif
if (mode_all)
{
log("Running hier_tree::ElaborateAll().\n");
- VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1);
Array veri_libs, vhdl_libs;
+#ifdef VERIFIC_VHDL_SUPPORT
+ VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
+#endif
if (veri_lib) veri_libs.InsertLast(veri_lib);
Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &parameters);
@@ -2834,7 +3159,9 @@ struct VerificPass : public Pass {
cmd_error(args, argidx, "No top module specified.\n");
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
+#ifdef VERIFIC_VHDL_SUPPORT
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
+#endif
Array veri_modules, vhdl_units;
for (; argidx < GetSize(args); argidx++)
@@ -2848,14 +3175,14 @@ struct VerificPass : public Pass {
veri_modules.InsertLast(veri_module);
continue;
}
-
+#ifdef VERIFIC_VHDL_SUPPORT
VhdlDesignUnit *vhdl_unit = vhdl_lib ? vhdl_lib->GetPrimUnit(name) : nullptr;
if (vhdl_unit) {
log("Adding VHDL unit '%s' to elaboration queue.\n", name);
vhdl_units.InsertLast(vhdl_unit);
continue;
}
-
+#endif
log_error("Can't find module/unit '%s'.\n", name);
}
@@ -2915,7 +3242,9 @@ struct VerificPass : public Pass {
}
veri_file::Reset();
+#ifdef VERIFIC_VHDL_SUPPORT
vhdl_file::Reset();
+#endif
Libset::Reset();
verific_incdirs.clear();
verific_libdirs.clear();
@@ -2923,6 +3252,65 @@ struct VerificPass : public Pass {
goto check_error;
}
+ if (argidx < GetSize(args) && args[argidx] == "-cfg")
+ {
+ if (argidx+1 == GetSize(args)) {
+ MapIter mi;
+ const char *k, *s;
+ unsigned long v;
+ pool<std::string> lines;
+ FOREACH_MAP_ITEM(RuntimeFlags::GetVarMap(), mi, &k, &v) {
+ lines.insert(stringf("%s %lu", k, v));
+ }
+ FOREACH_MAP_ITEM(RuntimeFlags::GetStringVarMap(), mi, &k, &s) {
+ if (s == nullptr)
+ lines.insert(stringf("%s NULL", k));
+ else
+ lines.insert(stringf("%s \"%s\"", k, s));
+ }
+ lines.sort();
+ for (auto &line : lines)
+ log("verific -cfg %s\n", line.c_str());
+ goto check_error;
+ }
+
+ if (argidx+2 == GetSize(args)) {
+ const char *k = args[argidx+1].c_str();
+ if (RuntimeFlags::HasUnsignedVar(k)) {
+ log("verific -cfg %s %lu\n", k, RuntimeFlags::GetVar(k));
+ goto check_error;
+ }
+ if (RuntimeFlags::HasStringVar(k)) {
+ const char *s = RuntimeFlags::GetStringVar(k);
+ if (s == nullptr)
+ log("verific -cfg %s NULL\n", k);
+ else
+ log("verific -cfg %s \"%s\"\n", k, s);
+ goto check_error;
+ }
+ log_cmd_error("Can't find Verific Runtime flag '%s'.\n", k);
+ }
+
+ if (argidx+3 == GetSize(args)) {
+ const auto &k = args[argidx+1], &v = args[argidx+2];
+ if (v == "NULL") {
+ RuntimeFlags::SetStringVar(k.c_str(), nullptr);
+ goto check_error;
+ }
+ if (v[0] == '"') {
+ std::string s = v.substr(1, GetSize(v)-2);
+ RuntimeFlags::SetStringVar(k.c_str(), v.c_str());
+ goto check_error;
+ }
+ char *endptr;
+ unsigned long n = strtol(v.c_str(), &endptr, 0);
+ if (*endptr == 0) {
+ RuntimeFlags::SetVar(k.c_str(), n);
+ goto check_error;
+ }
+ }
+ }
+
cmd_error(args, argidx, "Missing or unsupported mode parameter.\n");
check_error:
@@ -2958,11 +3346,19 @@ struct ReadPass : public Pass {
log("the language version (and before file names) to set additional verilog defines.\n");
log("\n");
log("\n");
+#ifdef VERIFIC_VHDL_SUPPORT
log(" read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} <vhdl-file>..\n");
log("\n");
log("Load the specified VHDL files. (Requires Verific.)\n");
log("\n");
log("\n");
+#endif
+ log(" read {-f|-F} <command-file>\n");
+ log("\n");
+ log("Load and execute the specified command file. (Requires Verific.)\n");
+ log("Check verific command for more information about supported commands in file.\n");
+ log("\n");
+ log("\n");
log(" read -define <macro>[=<value>]..\n");
log("\n");
log("Set global Verilog/SystemVerilog defines.\n");
@@ -3039,6 +3435,7 @@ struct ReadPass : public Pass {
return;
}
+#ifdef VERIFIC_VHDL_SUPPORT
if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl") {
if (use_verific) {
args[0] = "verific";
@@ -3048,6 +3445,16 @@ struct ReadPass : public Pass {
}
return;
}
+#endif
+ if (args[1] == "-f" || args[1] == "-F") {
+ if (use_verific) {
+ args[0] = "verific";
+ Pass::call(design, args);
+ } else {
+ cmd_error(args, 1, "This version of Yosys is built without Verific support.\n");
+ }
+ return;
+ }
if (args[1] == "-define") {
if (use_verific) {
diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h
index f168a2588..9d5beb787 100644
--- a/frontends/verific/verific.h
+++ b/frontends/verific/verific.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -50,6 +50,7 @@ struct VerificClocking {
RTLIL::Cell *addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const init_value = Const());
RTLIL::Cell *addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec sig_d, SigSpec sig_q, Const arst_value);
RTLIL::Cell *addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, SigSpec sig_d, SigSpec sig_q);
+ RTLIL::Cell *addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::SigSpec sig_adata, SigSpec sig_d, SigSpec sig_q);
bool property_matches_sequence(const VerificClocking &seq) const {
if (clock_net != seq.clock_net)
diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc
index 1f5da1b1d..1bbdcf016 100644
--- a/frontends/verific/verificsva.cc
+++ b/frontends/verific/verificsva.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc
index 230dfadbf..a4dfbc7ec 100644
--- a/frontends/verilog/const2ast.cc
+++ b/frontends/verilog/const2ast.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc
index 84966e501..883531e78 100644
--- a/frontends/verilog/preproc.cc
+++ b/frontends/verilog/preproc.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -36,6 +36,7 @@
#include "verilog_frontend.h"
#include "kernel/log.h"
#include <assert.h>
+#include <stack>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
@@ -141,6 +142,16 @@ static std::string next_token(bool pass_newline = false)
return_char(ch);
}
}
+ else if (ch == '\\')
+ {
+ while ((ch = next_char()) != 0) {
+ if (ch < 33 || ch > 126) {
+ return_char(ch);
+ break;
+ }
+ token += ch;
+ }
+ }
else if (ch == '/')
{
if ((ch = next_char()) != 0) {
@@ -334,6 +345,11 @@ define_map_t::add(const std::string &name, const std::string &txt, const arg_map
defines[name] = std::unique_ptr<define_body_t>(new define_body_t(txt, args));
}
+void define_map_t::add(const std::string &name, const define_body_t &body)
+{
+ defines[name] = std::unique_ptr<define_body_t>(new define_body_t(body));
+}
+
void define_map_t::merge(const define_map_t &map)
{
for (const auto &pr : map.defines) {
@@ -440,7 +456,17 @@ static bool read_argument(std::string &dest)
}
}
-static bool try_expand_macro(define_map_t &defines, std::string &tok)
+using macro_arg_stack_t = std::stack<std::pair<std::string, define_body_t>>;
+
+static void restore_macro_arg(define_map_t &defines, macro_arg_stack_t &macro_arg_stack)
+{
+ log_assert(!macro_arg_stack.empty());
+ auto &overwritten_arg = macro_arg_stack.top();
+ defines.add(overwritten_arg.first, overwritten_arg.second);
+ macro_arg_stack.pop();
+}
+
+static bool try_expand_macro(define_map_t &defines, macro_arg_stack_t &macro_arg_stack, std::string &tok)
{
if (tok == "`\"") {
std::string literal("\"");
@@ -450,7 +476,7 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok)
if (ntok == "`\"") {
insert_input(literal+"\"");
return true;
- } else if (!try_expand_macro(defines, ntok)) {
+ } else if (!try_expand_macro(defines, macro_arg_stack, ntok)) {
literal += ntok;
}
}
@@ -495,6 +521,10 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok)
args.push_back(arg);
}
for (const auto &pr : body->args.get_vals(name, args)) {
+ if (const define_body_t *existing = defines.find(pr.first)) {
+ macro_arg_stack.push({pr.first, *existing});
+ insert_input("`__restore_macro_arg ");
+ }
defines.add(pr.first, pr.second);
}
} else {
@@ -725,9 +755,18 @@ frontend_verilog_preproc(std::istream &f,
defines.merge(pre_defines);
defines.merge(global_defines_cache);
+ macro_arg_stack_t macro_arg_stack;
std::vector<std::string> filename_stack;
+ // We are inside pass_level levels of satisfied ifdefs, and then within
+ // fail_level levels of unsatisfied ifdefs. The unsatisfied ones are
+ // always within satisfied ones — even if some condition within is true,
+ // the parent condition failing renders it moot.
int ifdef_fail_level = 0;
int ifdef_pass_level = 0;
+ // For the outermost unsatisfied ifdef, true iff that ifdef already
+ // had a satisfied branch, and further elsif/else branches should be
+ // considered unsatisfied even if the condition is true.
+ // Meaningless if ifdef_fail_level == 0.
bool ifdef_already_satisfied = false;
output_code.clear();
@@ -745,7 +784,7 @@ frontend_verilog_preproc(std::istream &f,
if (ifdef_fail_level > 0)
ifdef_fail_level--;
else if (ifdef_pass_level > 0)
- ifdef_already_satisfied = --ifdef_pass_level;
+ ifdef_pass_level--;
else
log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
continue;
@@ -755,8 +794,9 @@ frontend_verilog_preproc(std::istream &f,
if (ifdef_fail_level == 0) {
if (ifdef_pass_level == 0)
log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
- log_assert(ifdef_already_satisfied);
+ ifdef_pass_level--;
ifdef_fail_level = 1;
+ ifdef_already_satisfied = true;
} else if (ifdef_fail_level == 1 && !ifdef_already_satisfied) {
ifdef_fail_level = 0;
ifdef_pass_level++;
@@ -771,8 +811,9 @@ frontend_verilog_preproc(std::istream &f,
if (ifdef_fail_level == 0) {
if (ifdef_pass_level == 0)
log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
- log_assert(ifdef_already_satisfied);
+ ifdef_pass_level--;
ifdef_fail_level = 1;
+ ifdef_already_satisfied = true;
} else if (ifdef_fail_level == 1 && !ifdef_already_satisfied && defines.find(name)) {
ifdef_fail_level = 0;
ifdef_pass_level++;
@@ -818,7 +859,7 @@ frontend_verilog_preproc(std::istream &f,
if (tok == "`include") {
skip_spaces();
std::string fn = next_token(true);
- while (try_expand_macro(defines, fn)) {
+ while (try_expand_macro(defines, macro_arg_stack, fn)) {
fn = next_token();
}
while (1) {
@@ -925,12 +966,21 @@ frontend_verilog_preproc(std::istream &f,
continue;
}
- if (try_expand_macro(defines, tok))
+ if (tok == "`__restore_macro_arg") {
+ restore_macro_arg(defines, macro_arg_stack);
+ continue;
+ }
+
+ if (try_expand_macro(defines, macro_arg_stack, tok))
continue;
output_code.push_back(tok);
}
+ if (ifdef_fail_level > 0 || ifdef_pass_level > 0) {
+ log_error("Unterminated preprocessor conditional!\n");
+ }
+
std::string output;
for (auto &str : output_code)
output += str;
diff --git a/frontends/verilog/preproc.h b/frontends/verilog/preproc.h
index 673d633c0..330855a92 100644
--- a/frontends/verilog/preproc.h
+++ b/frontends/verilog/preproc.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -42,6 +42,7 @@ struct define_map_t
// Add a definition, overwriting any existing definition for name.
void add(const std::string &name, const std::string &txt, const arg_map_t *args = nullptr);
+ void add(const std::string &name, const define_body_t &body);
// Merge in another map of definitions (which take precedence
// over anything currently defined).
diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc
index e2aecd99b..9b277c6b9 100644
--- a/frontends/verilog/verilog_frontend.cc
+++ b/frontends/verilog/verilog_frontend.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -61,8 +61,6 @@ static void add_package_types(dict<std::string, AST::AstNode *> &user_types, std
}
}
}
- user_type_stack.clear();
- user_type_stack.push_back(new UserTypeMap());
}
struct VerilogFrontend : public Frontend {
@@ -484,6 +482,19 @@ struct VerilogFrontend : public Frontend {
// make package typedefs available to parser
add_package_types(pkg_user_types, design->verilog_packages);
+ UserTypeMap global_types_map;
+ for (auto def : design->verilog_globals) {
+ if (def->type == AST::AST_TYPEDEF) {
+ global_types_map[def->str] = def;
+ }
+ }
+
+ log_assert(user_type_stack.empty());
+ // use previous global typedefs as bottom level of user type stack
+ user_type_stack.push_back(std::move(global_types_map));
+ // add a new empty type map to allow overriding existing global definitions
+ user_type_stack.push_back(UserTypeMap());
+
frontend_verilog_yyset_lineno(1);
frontend_verilog_yyrestart(NULL);
frontend_verilog_yyparse();
@@ -506,6 +517,10 @@ struct VerilogFrontend : public Frontend {
if (!flag_nopp)
delete lexin;
+ // only the previous and new global type maps remain
+ log_assert(user_type_stack.size() == 2);
+ user_type_stack.clear();
+
delete current_ast;
current_ast = NULL;
diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h
index aa7881038..8454e7999 100644
--- a/frontends/verilog/verilog_frontend.h
+++ b/frontends/verilog/verilog_frontend.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -47,7 +47,7 @@ namespace VERILOG_FRONTEND
// names of locally typedef'ed types in a stack
typedef std::map<std::string, AST::AstNode*> UserTypeMap;
- extern std::vector<UserTypeMap *> user_type_stack;
+ extern std::vector<UserTypeMap> user_type_stack;
// names of package typedef'ed types
extern dict<std::string, AST::AstNode*> pkg_user_types;
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index 66772a097..89c1aa895 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -37,6 +37,8 @@
#ifdef __clang__
// bison generates code using the 'register' storage class specifier
#pragma clang diagnostic ignored "-Wdeprecated-register"
+// flex generates weirdly-indented code
+#pragma clang diagnostic ignored "-Wmisleading-indentation"
#endif
#include "kernel/log.h"
@@ -103,7 +105,7 @@ static bool isUserType(std::string &s)
{
// check current scope then outer scopes for a name
for (auto it = user_type_stack.rbegin(); it != user_type_stack.rend(); ++it) {
- if ((*it)->count(s) > 0) {
+ if (it->count(s) > 0) {
return true;
}
}
@@ -260,6 +262,7 @@ static bool isUserType(std::string &s)
"const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); }
"checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); }
"endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); }
+"bind" { if (formal_mode) return TOK_BIND; SV_KEYWORD(TOK_BIND); }
"final" { SV_KEYWORD(TOK_FINAL); }
"logic" { SV_KEYWORD(TOK_LOGIC); }
"var" { SV_KEYWORD(TOK_VAR); }
@@ -276,8 +279,11 @@ static bool isUserType(std::string &s)
"output" { return TOK_OUTPUT; }
"inout" { return TOK_INOUT; }
"wire" { return TOK_WIRE; }
+"tri" { return TOK_WIRE; }
"wor" { return TOK_WOR; }
+"trior" { return TOK_WOR; }
"wand" { return TOK_WAND; }
+"triand" { return TOK_WAND; }
"reg" { return TOK_REG; }
"integer" { return TOK_INTEGER; }
"signed" { return TOK_SIGNED; }
@@ -431,8 +437,13 @@ supply1 { return TOK_SUPPLY1; }
"/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" {
static bool printed_warning = false;
if (!printed_warning) {
- log_warning("Found one of those horrible `(synopsys|synthesis) translate_off' comments.\n"
- "Yosys does support them but it is recommended to use `ifdef constructs instead!\n");
+ log_warning(
+ "Encountered `translate_off' comment! Such legacy hot "
+ "comments are supported by Yosys, but are not part of "
+ "any formal language specification. Using a portable "
+ "and standards-compliant construct such as `ifdef is "
+ "recommended!\n"
+ );
printed_warning = true;
}
BEGIN(SYNOPSYS_TRANSLATE_OFF);
@@ -447,8 +458,13 @@ supply1 { return TOK_SUPPLY1; }
<SYNOPSYS_FLAGS>full_case {
static bool printed_warning = false;
if (!printed_warning) {
- log_warning("Found one of those horrible `(synopsys|synthesis) full_case' comments.\n"
- "Yosys does support them but it is recommended to use Verilog `full_case' attributes instead!\n");
+ log_warning(
+ "Encountered `full_case' comment! Such legacy hot "
+ "comments are supported by Yosys, but are not part of "
+ "any formal language specification. Using the Verilog "
+ "`full_case' attribute or the SystemVerilog `unique' "
+ "or `unique0' keywords is recommended!\n"
+ );
printed_warning = true;
}
return TOK_SYNOPSYS_FULL_CASE;
@@ -456,8 +472,13 @@ supply1 { return TOK_SUPPLY1; }
<SYNOPSYS_FLAGS>parallel_case {
static bool printed_warning = false;
if (!printed_warning) {
- log_warning("Found one of those horrible `(synopsys|synthesis) parallel_case' comments.\n"
- "Yosys does support them but it is recommended to use Verilog `parallel_case' attributes instead!\n");
+ log_warning(
+ "Encountered `parallel_case' comment! Such legacy hot "
+ "comments are supported by Yosys, but are not part of "
+ "any formal language specification. Using the Verilog "
+ "`parallel_case' attribute or the SystemVerilog "
+ "`unique' or `priority' keywords is recommended!\n"
+ );
printed_warning = true;
}
return TOK_SYNOPSYS_PARALLEL_CASE;
@@ -529,11 +550,18 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
".*" { return TOK_WILDCARD_CONNECT; }
-"|=" { SV_KEYWORD(TOK_OR_ASSIGN); }
-"&=" { SV_KEYWORD(TOK_AND_ASSIGN); }
-"+=" { SV_KEYWORD(TOK_PLUS_ASSIGN); }
+"|=" { SV_KEYWORD(TOK_BIT_OR_ASSIGN); }
+"&=" { SV_KEYWORD(TOK_BIT_AND_ASSIGN); }
+"+=" { SV_KEYWORD(TOK_ADD_ASSIGN); }
"-=" { SV_KEYWORD(TOK_SUB_ASSIGN); }
-"^=" { SV_KEYWORD(TOK_XOR_ASSIGN); }
+"^=" { SV_KEYWORD(TOK_BIT_XOR_ASSIGN); }
+"/=" { SV_KEYWORD(TOK_DIV_ASSIGN); }
+"%=" { SV_KEYWORD(TOK_MOD_ASSIGN); }
+"*=" { SV_KEYWORD(TOK_MUL_ASSIGN); }
+"<<=" { SV_KEYWORD(TOK_SHL_ASSIGN); }
+">>=" { SV_KEYWORD(TOK_SHR_ASSIGN); }
+"<<<=" { SV_KEYWORD(TOK_SSHL_ASSIGN); }
+">>>=" { SV_KEYWORD(TOK_SSHR_ASSIGN); }
[-+]?[=*]> {
if (!specify_mode) REJECT;
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index bcba9b76a..171e098a5 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -33,6 +33,8 @@
*
*/
+%require "3.0"
+
%{
#include <list>
#include <stack>
@@ -54,7 +56,7 @@ namespace VERILOG_FRONTEND {
dict<IdString, AstNode*> *attr_list, default_attr_list;
std::stack<dict<IdString, AstNode*> *> attr_list_stack;
dict<IdString, AstNode*> *albuf;
- std::vector<UserTypeMap*> user_type_stack;
+ std::vector<UserTypeMap> user_type_stack;
dict<std::string, AstNode*> pkg_user_types;
std::vector<AstNode*> ast_stack;
struct AstNode *astbuf1, *astbuf2, *astbuf3;
@@ -127,13 +129,22 @@ struct specify_rise_fall {
specify_triple fall;
};
+static void addWiretypeNode(std::string *name, AstNode *node)
+{
+ log_assert(node);
+ node->is_custom_type = true;
+ node->children.push_back(new AstNode(AST_WIRETYPE));
+ node->children.back()->str = *name;
+ delete name;
+}
+
static void addTypedefNode(std::string *name, AstNode *node)
{
log_assert(node);
auto *tnode = new AstNode(AST_TYPEDEF, node);
tnode->str = *name;
- auto user_types = user_type_stack.back();
- (*user_types)[*name] = tnode;
+ auto &user_types = user_type_stack.back();
+ user_types[*name] = tnode;
if (current_ast_mod && current_ast_mod->type == AST_PACKAGE) {
// typedef inside a package so we need the qualified name
auto qname = current_ast_mod->str + "::" + (*name).substr(1);
@@ -145,8 +156,7 @@ static void addTypedefNode(std::string *name, AstNode *node)
static void enterTypeScope()
{
- auto user_types = new UserTypeMap();
- user_type_stack.push_back(user_types);
+ user_type_stack.push_back(UserTypeMap());
}
static void exitTypeScope()
@@ -157,18 +167,24 @@ static void exitTypeScope()
static bool isInLocalScope(const std::string *name)
{
// tests if a name was declared in the current block scope
- auto user_types = user_type_stack.back();
- return (user_types->count(*name) > 0);
+ auto &user_types = user_type_stack.back();
+ return (user_types.count(*name) > 0);
}
static AstNode *getTypeDefinitionNode(std::string type_name)
{
- // return the definition nodes from the typedef statement
- auto user_types = user_type_stack.back();
- log_assert(user_types->count(type_name) > 0);
- auto typedef_node = (*user_types)[type_name];
- log_assert(typedef_node->type == AST_TYPEDEF);
- return typedef_node->children[0];
+ // check current scope then outer scopes for a name
+ for (auto it = user_type_stack.rbegin(); it != user_type_stack.rend(); ++it) {
+ if (it->count(type_name) > 0) {
+ // return the definition nodes from the typedef statement
+ auto typedef_node = (*it)[type_name];
+ log_assert(typedef_node->type == AST_TYPEDEF);
+ return typedef_node->children[0];
+ }
+ }
+
+ // The lexer recognized the name as a TOK_USER_TYPE, but now we can't find it anymore?
+ log_error("typedef for user type `%s' not found", type_name.c_str());
}
static AstNode *copyTypeDefinition(std::string type_name)
@@ -230,6 +246,75 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode)
node->children.push_back(rangeNode);
}
+static void checkLabelsMatch(const char *element, const std::string *before, const std::string *after)
+{
+ if (!before && after)
+ frontend_verilog_yyerror("%s missing where end label (%s) was given.",
+ element, after->c_str() + 1);
+ if (before && after && *before != *after)
+ frontend_verilog_yyerror("%s (%s) and end label (%s) don't match.",
+ element, before->c_str() + 1, after->c_str() + 1);
+}
+
+// This transforms a loop like
+// for (genvar i = 0; i < 10; i++) begin : blk
+// to
+// genvar _i;
+// for (_i = 0; _i < 10; _i++) begin : blk
+// localparam i = _i;
+// where `_i` is actually some auto-generated name.
+static void rewriteGenForDeclInit(AstNode *loop)
+{
+ // check if this generate for loop contains an inline declaration
+ log_assert(loop->type == AST_GENFOR);
+ AstNode *decl = loop->children[0];
+ if (decl->type == AST_ASSIGN_EQ)
+ return;
+ log_assert(decl->type == AST_GENVAR);
+ log_assert(loop->children.size() == 5);
+
+ // identify each component of the loop
+ AstNode *init = loop->children[1];
+ AstNode *cond = loop->children[2];
+ AstNode *incr = loop->children[3];
+ AstNode *body = loop->children[4];
+ log_assert(init->type == AST_ASSIGN_EQ);
+ log_assert(incr->type == AST_ASSIGN_EQ);
+ log_assert(body->type == AST_GENBLOCK);
+
+ // create a unique name for the genvar
+ std::string old_str = decl->str;
+ std::string new_str = stringf("$genfordecl$%d$%s", autoidx++, old_str.c_str());
+
+ // rename and move the genvar declaration to the containing description
+ decl->str = new_str;
+ loop->children.erase(loop->children.begin());
+ log_assert(current_ast_mod != nullptr);
+ current_ast_mod->children.push_back(decl);
+
+ // create a new localparam with old name so that the items in the loop
+ // can simply use the old name and shadow it as necessary
+ AstNode *indirect = new AstNode(AST_LOCALPARAM);
+ indirect->str = old_str;
+ AstNode *ident = new AstNode(AST_IDENTIFIER);
+ ident->str = new_str;
+ indirect->children.push_back(ident);
+
+ body->children.insert(body->children.begin(), indirect);
+
+ // only perform the renaming for the initialization, guard, and
+ // incrementation to enable proper shadowing of the synthetic localparam
+ std::function<void(AstNode*)> substitute = [&](AstNode *node) {
+ if (node->type == AST_IDENTIFIER && node->str == old_str)
+ node->str = new_str;
+ for (AstNode *child : node->children)
+ substitute(child);
+ };
+ substitute(init);
+ substitute(cond);
+ substitute(incr);
+}
+
%}
%define api.prefix {frontend_verilog_yy}
@@ -254,6 +339,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode)
bool boolean;
char ch;
int integer;
+ YOSYS_NAMESPACE_PREFIX AST::AstNodeType ast_node_type;
}
%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE
@@ -266,7 +352,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode)
%token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP
%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR TOK_WILDCARD_CONNECT
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC
-%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_PLUS_ASSIGN TOK_ALWAYS TOK_INITIAL
+%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
%token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC
@@ -280,18 +366,23 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode)
%token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY
%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_UNIQUE0 TOK_PRIORITY
%token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE TOK_SHORTINT TOK_LONGINT TOK_UNION
-%token TOK_OR_ASSIGN TOK_XOR_ASSIGN TOK_AND_ASSIGN TOK_SUB_ASSIGN
+%token TOK_BIT_OR_ASSIGN TOK_BIT_AND_ASSIGN TOK_BIT_XOR_ASSIGN TOK_ADD_ASSIGN
+%token TOK_SUB_ASSIGN TOK_DIV_ASSIGN TOK_MOD_ASSIGN TOK_MUL_ASSIGN
+%token TOK_SHL_ASSIGN TOK_SHR_ASSIGN TOK_SSHL_ASSIGN TOK_SSHR_ASSIGN
+%token TOK_BIND
%type <ast> range range_or_multirange non_opt_range non_opt_multirange
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type
%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number
%type <string> type_name
-%type <ast> opt_enum_init enum_type struct_type non_wire_data_type func_return_type
+%type <ast> opt_enum_init enum_type struct_type enum_struct_type func_return_type typedef_base_type
%type <boolean> opt_property always_comb_or_latch always_or_always_ff
%type <boolean> opt_signedness_default_signed opt_signedness_default_unsigned
-%type <integer> integer_atom_type
+%type <integer> integer_atom_type integer_vector_type
%type <al> attr case_attr
%type <ast> struct_union
+%type <ast_node_type> asgn_binop
+%type <ast> genvar_identifier
%type <specify_target_ptr> specify_target
%type <specify_triple_ptr> specify_triple specify_opt_triple
@@ -345,6 +436,7 @@ design:
typedef_decl design |
package design |
interface design |
+ bind_directive design |
%empty;
attr:
@@ -448,7 +540,6 @@ module:
port_counter = 0;
mod->str = *$4;
append_attr(mod, $1);
- delete $4;
} module_para_opt module_args_opt ';' module_body TOK_ENDMODULE opt_label {
if (port_stubs.size() != 0)
frontend_verilog_yyerror("Missing details for module port `%s'.",
@@ -456,7 +547,10 @@ module:
SET_AST_NODE_LOC(ast_stack.back(), @2, @$);
ast_stack.pop_back();
log_assert(ast_stack.size() == 1);
+ checkLabelsMatch("Module name", $4, $11);
current_ast_mod = NULL;
+ delete $4;
+ delete $11;
exitTypeScope();
};
@@ -494,18 +588,19 @@ optional_comma:
module_arg_opt_assignment:
'=' expr {
if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) {
- AstNode *wire = new AstNode(AST_IDENTIFIER);
- wire->str = ast_stack.back()->children.back()->str;
if (ast_stack.back()->children.back()->is_input) {
AstNode *n = ast_stack.back()->children.back();
if (n->attributes.count(ID::defaultvalue))
delete n->attributes.at(ID::defaultvalue);
n->attributes[ID::defaultvalue] = $2;
- } else
- if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic)
- ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2))));
- else
- ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2));
+ } else {
+ AstNode *wire = new AstNode(AST_IDENTIFIER);
+ wire->str = ast_stack.back()->children.back()->str;
+ if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic)
+ ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2))));
+ else
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2));
+ }
} else
frontend_verilog_yyerror("SystemVerilog interface in module port list cannot have a default value.");
} |
@@ -573,7 +668,10 @@ package:
append_attr(mod, $1);
} ';' package_body TOK_ENDPACKAGE opt_label {
ast_stack.pop_back();
+ checkLabelsMatch("Package name", $4, $9);
current_ast_mod = NULL;
+ delete $4;
+ delete $9;
exitTypeScope();
};
@@ -581,7 +679,7 @@ package_body:
package_body package_body_stmt | %empty;
package_body_stmt:
- typedef_decl | localparam_decl | param_decl;
+ typedef_decl | localparam_decl | param_decl | task_func_decl;
interface:
TOK_INTERFACE {
@@ -611,7 +709,67 @@ interface_body:
interface_body_stmt:
param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
- modport_stmt;
+ modport_stmt | bind_directive;
+
+bind_directive:
+ TOK_BIND {
+ AstNode *bnode = new AstNode(AST_BIND);
+ ast_stack.back()->children.push_back(bnode);
+ ast_stack.push_back(bnode);
+ }
+ bind_target {
+ // bind_target should have added at least one child
+ log_assert(ast_stack.back()->children.size() >= 1);
+ }
+ TOK_ID {
+ // The single_cell parser in cell_list_no_array uses astbuf1 as
+ // a sort of template for constructing cells.
+ astbuf1 = new AstNode(AST_CELL);
+ astbuf1->children.push_back(new AstNode(AST_CELLTYPE));
+ astbuf1->children[0]->str = *$5;
+ delete $5;
+ }
+ cell_parameter_list_opt cell_list_no_array ';' {
+ // cell_list should have added at least one more child
+ log_assert(ast_stack.back()->children.size() >= 2);
+ delete astbuf1;
+ ast_stack.pop_back();
+ };
+
+// bind_target matches the target of the bind (everything before
+// bind_instantiation in the IEEE 1800 spec).
+//
+// We can't use the BNF from the spec directly because it's ambiguous:
+// something like "bind foo bar_i (.*)" can either be interpreted with "foo" as
+// a module or interface identifier (matching bind_target_scope in the spec) or
+// by considering foo as a degenerate hierarchical identifier with no '.'
+// characters, followed by no bit select (which matches bind_target_instance in
+// the spec).
+//
+// Instead, we resolve everything as an instance name and then deal with the
+// ambiguity when converting to RTLIL / in the hierarchy pass.
+bind_target:
+ bind_target_instance opt_bind_target_instance_list;
+
+// An optional list of target instances for a bind statement, introduced by a
+// colon.
+opt_bind_target_instance_list:
+ ':' bind_target_instance_list |
+ %empty;
+
+bind_target_instance_list:
+ bind_target_instance |
+ bind_target_instance_list ',' bind_target_instance;
+
+// A single target instance for a bind statement. The top of ast_stack will be
+// the bind node where we should add it.
+bind_target_instance:
+ hierarchical_id {
+ auto *node = new AstNode(AST_IDENTIFIER);
+ node->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(node);
+ };
mintypmax_expr:
expr { delete $1; } |
@@ -676,21 +834,10 @@ opt_wire_type_token:
wire_type_token | %empty;
wire_type_token:
- hierarchical_type_id {
- astbuf3->is_custom_type = true;
- astbuf3->children.push_back(new AstNode(AST_WIRETYPE));
- astbuf3->children.back()->str = *$1;
- } |
- TOK_WOR {
- astbuf3->is_wor = true;
- } |
- TOK_WAND {
- astbuf3->is_wand = true;
- } |
- // wires
- TOK_WIRE {
+ // nets
+ net_type {
} |
- TOK_WIRE logic_type {
+ net_type logic_type {
} |
// regs
TOK_REG {
@@ -717,6 +864,15 @@ wire_type_token:
astbuf3->range_right = 0;
};
+net_type:
+ TOK_WOR {
+ astbuf3->is_wor = true;
+ } |
+ TOK_WAND {
+ astbuf3->is_wand = true;
+ } |
+ TOK_WIRE;
+
logic_type:
TOK_LOGIC {
} |
@@ -724,6 +880,9 @@ logic_type:
astbuf3->range_left = $1 - 1;
astbuf3->range_right = 0;
astbuf3->is_signed = true;
+ } |
+ hierarchical_type_id {
+ addWiretypeNode($1, astbuf3);
};
integer_atom_type:
@@ -733,6 +892,10 @@ integer_atom_type:
TOK_LONGINT { $$ = 64; } |
TOK_BYTE { $$ = 8; } ;
+integer_vector_type:
+ TOK_LOGIC { $$ = TOK_LOGIC; } |
+ TOK_REG { $$ = TOK_REG; } ;
+
non_opt_range:
'[' expr ':' expr ']' {
$$ = new AstNode(AST_RANGE);
@@ -787,7 +950,7 @@ module_body:
module_body_stmt:
task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
- enum_decl | struct_decl |
+ enum_decl | struct_decl | bind_directive |
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
checker_decl:
@@ -861,6 +1024,7 @@ task_func_decl:
outreg->children.push_back($4);
outreg->is_signed = $4->is_signed;
$4->is_signed = false;
+ outreg->is_custom_type = $4->type == AST_WIRETYPE;
}
current_function_or_task->children.push_back(outreg);
current_function_or_task_port_id = 1;
@@ -871,6 +1035,11 @@ task_func_decl:
};
func_return_type:
+ hierarchical_type_id {
+ $$ = new AstNode(AST_WIRETYPE);
+ $$->str = *$1;
+ delete $1;
+ } |
opt_type_vec opt_signedness_default_unsigned {
$$ = makeRange(0, 0, $2);
} |
@@ -1141,6 +1310,8 @@ specify_item:
cell->children.back()->str = "\\DST";
delete $1;
+ delete limit;
+ delete limit2;
};
specify_opt_triple:
@@ -1438,6 +1609,7 @@ param_type:
astbuf1->is_custom_type = true;
astbuf1->children.push_back(new AstNode(AST_WIRETYPE));
astbuf1->children.back()->str = *$1;
+ delete $1;
};
param_decl:
@@ -1462,7 +1634,26 @@ param_decl_list:
single_param_decl | param_decl_list ',' single_param_decl;
single_param_decl:
- TOK_ID '=' expr {
+ single_param_decl_ident '=' expr {
+ AstNode *decl = ast_stack.back()->children.back();
+ log_assert(decl->type == AST_PARAMETER || decl->type == AST_LOCALPARAM);
+ delete decl->children[0];
+ decl->children[0] = $3;
+ } |
+ single_param_decl_ident {
+ AstNode *decl = ast_stack.back()->children.back();
+ if (decl->type != AST_PARAMETER) {
+ log_assert(decl->type == AST_LOCALPARAM);
+ frontend_verilog_yyerror("localparam initialization is missing!");
+ }
+ if (!sv_mode)
+ frontend_verilog_yyerror("Parameter defaults can only be omitted in SystemVerilog mode!");
+ delete decl->children[0];
+ decl->children.erase(decl->children.begin());
+ };
+
+single_param_decl_ident:
+ TOK_ID {
AstNode *node;
if (astbuf1 == nullptr) {
if (!sv_mode)
@@ -1473,10 +1664,9 @@ single_param_decl:
node = astbuf1->clone();
}
node->str = *$1;
- delete node->children[0];
- node->children[0] = $3;
ast_stack.back()->children.push_back(node);
delete $1;
+ SET_AST_NODE_LOC(node, @1, @1);
};
defparam_decl:
@@ -1509,17 +1699,18 @@ enum_type: TOK_ENUM {
// create the template for the names
astbuf1 = new AstNode(AST_ENUM_ITEM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
- } enum_base_type '{' enum_name_list '}' { // create template for the enum vars
- auto tnode = astbuf1->clone();
- delete astbuf1;
- astbuf1 = tnode;
- tnode->type = AST_WIRE;
- tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str);
- // drop constant but keep any range
- delete tnode->children[0];
- tnode->children.erase(tnode->children.begin());
- $$ = astbuf1; }
- ;
+ } enum_base_type '{' enum_name_list optional_comma '}' {
+ // create template for the enum vars
+ auto tnode = astbuf1->clone();
+ delete astbuf1;
+ astbuf1 = tnode;
+ tnode->type = AST_WIRE;
+ tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str);
+ // drop constant but keep any range
+ delete tnode->children[0];
+ tnode->children.erase(tnode->children.begin());
+ $$ = astbuf1;
+ };
enum_base_type: type_atom type_signing
| type_vec type_signing range { if ($3) astbuf1->children.push_back($3); }
@@ -1658,10 +1849,12 @@ member_type_token:
delete astbuf1;
astbuf1 = template_node;
}
- | struct_union {
+ | {
+ delete astbuf1;
+ } struct_union {
// stash state on ast_stack
ast_stack.push_back(astbuf2);
- astbuf2 = $1;
+ astbuf2 = $2;
} struct_body {
astbuf1 = astbuf2;
// recover state
@@ -1867,7 +2060,7 @@ type_name: TOK_ID // first time seen
;
typedef_decl:
- TOK_TYPEDEF non_io_wire_type range type_name range_or_multirange ';' {
+ TOK_TYPEDEF typedef_base_type range type_name range_or_multirange ';' {
astbuf1 = $2;
astbuf2 = checkRange(astbuf1, $3);
if (astbuf2)
@@ -1880,10 +2073,33 @@ typedef_decl:
rewriteAsMemoryNode(astbuf1, $5);
}
addTypedefNode($4, astbuf1); }
- | TOK_TYPEDEF non_wire_data_type type_name ';' { addTypedefNode($3, $2); }
+ | TOK_TYPEDEF enum_struct_type type_name ';' { addTypedefNode($3, $2); }
;
-non_wire_data_type:
+typedef_base_type:
+ hierarchical_type_id {
+ $$ = new AstNode(AST_WIRE);
+ $$->is_logic = true;
+ addWiretypeNode($1, $$);
+ } |
+ integer_vector_type opt_signedness_default_unsigned {
+ $$ = new AstNode(AST_WIRE);
+ if ($1 == TOK_REG) {
+ $$->is_reg = true;
+ } else {
+ $$->is_logic = true;
+ }
+ $$->is_signed = $2;
+ } |
+ integer_atom_type opt_signedness_default_signed {
+ $$ = new AstNode(AST_WIRE);
+ $$->is_logic = true;
+ $$->is_signed = $2;
+ $$->range_left = $1 - 1;
+ $$->range_right = 0;
+ };
+
+enum_struct_type:
enum_type
| struct_type
;
@@ -1920,6 +2136,9 @@ cell_list:
cell_list ',' single_cell;
single_cell:
+ single_cell_no_array | single_cell_arraylist;
+
+single_cell_no_array:
TOK_ID {
astbuf2 = astbuf1->clone();
if (astbuf2->type != AST_PRIMITIVE)
@@ -1928,7 +2147,9 @@ single_cell:
ast_stack.back()->children.push_back(astbuf2);
} '(' cell_port_list ')' {
SET_AST_NODE_LOC(astbuf2, @1, @$);
- } |
+ }
+
+single_cell_arraylist:
TOK_ID non_opt_range {
astbuf2 = astbuf1->clone();
if (astbuf2->type != AST_PRIMITIVE)
@@ -1939,6 +2160,10 @@ single_cell:
SET_AST_NODE_LOC(astbuf2, @1, @$);
};
+cell_list_no_array:
+ single_cell_no_array |
+ cell_list_no_array ',' single_cell_no_array;
+
prim_list:
single_prim |
prim_list ',' single_prim;
@@ -2046,6 +2271,7 @@ cell_port:
if (!sv_mode)
frontend_verilog_yyerror("Wildcard port connections are only supported in SystemVerilog mode.");
astbuf2->attributes[ID::wildcard_port_conns] = AstNode::mkconst_int(1, false);
+ free_attr($1);
};
always_comb_or_latch:
@@ -2417,45 +2643,81 @@ simple_behavioral_stmt:
SET_AST_NODE_LOC(node, @2, @5);
append_attr(node, $1);
} |
- attr lvalue TOK_XOR_ASSIGN delay expr {
- AstNode *xor_node = new AstNode(AST_BIT_XOR, $2->clone(), $5);
- AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, xor_node);
- SET_AST_NODE_LOC(xor_node, @2, @5);
- SET_AST_NODE_LOC(node, @2, @5);
- ast_stack.back()->children.push_back(node);
- append_attr(node, $1);
- } |
- attr lvalue TOK_OR_ASSIGN delay expr {
- AstNode *or_node = new AstNode(AST_BIT_OR, $2->clone(), $5);
- SET_AST_NODE_LOC(or_node, @2, @5);
- AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, or_node);
+ attr lvalue asgn_binop delay expr {
+ AstNode *expr_node = $5;
+ if ($3 == AST_SHIFT_LEFT || $3 == AST_SHIFT_RIGHT ||
+ $3 == AST_SHIFT_SLEFT || $3 == AST_SHIFT_SRIGHT) {
+ expr_node = new AstNode(AST_TO_UNSIGNED, expr_node);
+ SET_AST_NODE_LOC(expr_node, @5, @5);
+ }
+ AstNode *op_node = new AstNode($3, $2->clone(), expr_node);
+ AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, op_node);
+ SET_AST_NODE_LOC(op_node, @2, @5);
SET_AST_NODE_LOC(node, @2, @5);
ast_stack.back()->children.push_back(node);
append_attr(node, $1);
- } |
- attr lvalue TOK_PLUS_ASSIGN delay expr {
- AstNode *add_node = new AstNode(AST_ADD, $2->clone(), $5);
- AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, add_node);
- SET_AST_NODE_LOC(node, @2, @5);
- SET_AST_NODE_LOC(add_node, @2, @5);
+ };
+
+asgn_binop:
+ TOK_BIT_OR_ASSIGN { $$ = AST_BIT_OR; } |
+ TOK_BIT_AND_ASSIGN { $$ = AST_BIT_AND; } |
+ TOK_BIT_XOR_ASSIGN { $$ = AST_BIT_XOR; } |
+ TOK_ADD_ASSIGN { $$ = AST_ADD; } |
+ TOK_SUB_ASSIGN { $$ = AST_SUB; } |
+ TOK_DIV_ASSIGN { $$ = AST_DIV; } |
+ TOK_MOD_ASSIGN { $$ = AST_MOD; } |
+ TOK_MUL_ASSIGN { $$ = AST_MUL; } |
+ TOK_SHL_ASSIGN { $$ = AST_SHIFT_LEFT; } |
+ TOK_SHR_ASSIGN { $$ = AST_SHIFT_RIGHT; } |
+ TOK_SSHL_ASSIGN { $$ = AST_SHIFT_SLEFT; } |
+ TOK_SSHR_ASSIGN { $$ = AST_SHIFT_SRIGHT; } ;
+
+for_initialization:
+ TOK_ID '=' expr {
+ AstNode *ident = new AstNode(AST_IDENTIFIER);
+ ident->str = *$1;
+ AstNode *node = new AstNode(AST_ASSIGN_EQ, ident, $3);
ast_stack.back()->children.push_back(node);
- append_attr(node, $1);
+ SET_AST_NODE_LOC(node, @1, @3);
+ delete $1;
} |
- attr lvalue TOK_SUB_ASSIGN delay expr {
- AstNode *sub_node = new AstNode(AST_SUB, $2->clone(), $5);
- AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, sub_node);
- SET_AST_NODE_LOC(node, @2, @5);
- SET_AST_NODE_LOC(sub_node, @2, @5);
- ast_stack.back()->children.push_back(node);
- append_attr(node, $1);
+ non_io_wire_type range TOK_ID {
+ frontend_verilog_yyerror("For loop variable declaration is missing initialization!");
} |
- attr lvalue TOK_AND_ASSIGN delay expr {
- AstNode *and_node = new AstNode(AST_BIT_AND, $2->clone(), $5);
- AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, and_node);
- SET_AST_NODE_LOC(node, @2, @5);
- SET_AST_NODE_LOC(and_node, @2, @5);
- ast_stack.back()->children.push_back(node);
- append_attr(node, $1);
+ non_io_wire_type range TOK_ID '=' expr {
+ if (!sv_mode)
+ frontend_verilog_yyerror("For loop inline variable declaration is only supported in SystemVerilog mode!");
+
+ // loop variable declaration
+ AstNode *wire = $1;
+ AstNode *range = checkRange(wire, $2);
+ if (range != nullptr)
+ wire->children.push_back(range);
+ SET_AST_NODE_LOC(wire, @1, @3);
+ SET_AST_NODE_LOC(range, @2, @2);
+
+ AstNode *ident = new AstNode(AST_IDENTIFIER);
+ ident->str = *$3;
+ wire->str = *$3;
+ delete $3;
+
+ AstNode *loop = ast_stack.back();
+ AstNode *parent = ast_stack.at(ast_stack.size() - 2);
+ log_assert(parent->children.back() == loop);
+
+ // loop variable initialization
+ AstNode *asgn = new AstNode(AST_ASSIGN_EQ, ident, $5);
+ loop->children.push_back(asgn);
+ SET_AST_NODE_LOC(asgn, @3, @5);
+ SET_AST_NODE_LOC(ident, @3, @3);
+
+ // inject a wrapping block to declare the loop variable and
+ // contain the current loop
+ AstNode *wrapper = new AstNode(AST_BLOCK);
+ wrapper->str = "$fordecl_block$" + std::to_string(autoidx++);
+ wrapper->children.push_back(wire);
+ wrapper->children.push_back(loop);
+ parent->children.back() = wrapper; // replaces `loop`
};
// this production creates the obligatory if-else shift/reduce conflict
@@ -2497,8 +2759,7 @@ behavioral_stmt:
node->str = *$4;
} behavioral_stmt_list TOK_END opt_label {
exitTypeScope();
- if ($4 != NULL && $8 != NULL && *$4 != *$8)
- frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1);
+ checkLabelsMatch("Begin label", $4, $8);
AstNode *node = ast_stack.back();
// In SystemVerilog, unnamed blocks with block item declarations
// create an implicit hierarchy scope
@@ -2519,7 +2780,7 @@ behavioral_stmt:
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
append_attr(node, $1);
- } simple_behavioral_stmt ';' expr {
+ } for_initialization ';' expr {
ast_stack.back()->children.push_back($7);
} ';' simple_behavioral_stmt ')' {
AstNode *block = new AstNode(AST_BLOCK);
@@ -2718,6 +2979,7 @@ rvalue:
hierarchical_id '[' expr ']' '.' rvalue {
$$ = new AstNode(AST_PREFIX, $3, $6);
$$->str = *$1;
+ SET_AST_NODE_LOC($$, @1, @6);
delete $1;
} |
hierarchical_id range {
@@ -2783,16 +3045,50 @@ gen_stmt_or_module_body_stmt:
free_attr($1);
};
+genvar_identifier:
+ TOK_ID {
+ $$ = new AstNode(AST_IDENTIFIER);
+ $$->str = *$1;
+ delete $1;
+ };
+
+genvar_initialization:
+ TOK_GENVAR genvar_identifier {
+ frontend_verilog_yyerror("Generate for loop variable declaration is missing initialization!");
+ } |
+ TOK_GENVAR genvar_identifier '=' expr {
+ if (!sv_mode)
+ frontend_verilog_yyerror("Generate for loop inline variable declaration is only supported in SystemVerilog mode!");
+ AstNode *node = new AstNode(AST_GENVAR);
+ node->is_reg = true;
+ node->is_signed = true;
+ node->range_left = 31;
+ node->range_right = 0;
+ node->str = $2->str;
+ node->children.push_back(checkRange(node, nullptr));
+ ast_stack.back()->children.push_back(node);
+ SET_AST_NODE_LOC(node, @1, @4);
+ node = new AstNode(AST_ASSIGN_EQ, $2, $4);
+ ast_stack.back()->children.push_back(node);
+ SET_AST_NODE_LOC(node, @1, @4);
+ } |
+ genvar_identifier '=' expr {
+ AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3);
+ ast_stack.back()->children.push_back(node);
+ SET_AST_NODE_LOC(node, @1, @3);
+ };
+
// this production creates the obligatory if-else shift/reduce conflict
gen_stmt:
TOK_FOR '(' {
AstNode *node = new AstNode(AST_GENFOR);
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
- } simple_behavioral_stmt ';' expr {
+ } genvar_initialization ';' expr {
ast_stack.back()->children.push_back($6);
} ';' simple_behavioral_stmt ')' gen_stmt_block {
SET_AST_NODE_LOC(ast_stack.back(), @1, @11);
+ rewriteGenForDeclInit(ast_stack.back());
ast_stack.pop_back();
} |
TOK_IF '(' expr ')' {
@@ -2834,8 +3130,7 @@ gen_block:
ast_stack.push_back(node);
} module_gen_body TOK_END opt_label {
exitTypeScope();
- if ($3 != NULL && $7 != NULL && *$3 != *$7)
- frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $3->c_str()+1, $7->c_str()+1);
+ checkLabelsMatch("Begin label", $3, $7);
delete $3;
delete $7;
SET_AST_NODE_LOC(ast_stack.back(), @1, @7);