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