aboutsummaryrefslogtreecommitdiffstats
path: root/frontends/ast
diff options
context:
space:
mode:
authorClaire Xen <claire@clairexen.net>2022-02-11 16:03:12 +0100
committerGitHub <noreply@github.com>2022-02-11 16:03:12 +0100
commit49545c73f7f5a5cf73d287fd371f2ff39311f621 (patch)
treed0f20b8def36e551c6735d4fc6033aaa2633fe80 /frontends/ast
parent90b40aa51f7d666792d4f0b1830ee75b81678a1f (diff)
parente0165188669fcef2c5784c9916683889a2164e5d (diff)
downloadyosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.tar.gz
yosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.tar.bz2
yosys-49545c73f7f5a5cf73d287fd371f2ff39311f621.zip
Merge branch 'master' into clk2ff-better-names
Diffstat (limited to 'frontends/ast')
-rw-r--r--frontends/ast/Makefile.inc1
-rw-r--r--frontends/ast/ast.cc311
-rw-r--r--frontends/ast/ast.h50
-rw-r--r--frontends/ast/ast_binding.cc49
-rw-r--r--frontends/ast/ast_binding.h58
-rw-r--r--frontends/ast/dpicall.cc2
-rw-r--r--frontends/ast/genrtlil.cc268
-rw-r--r--frontends/ast/simplify.cc688
8 files changed, 1200 insertions, 227 deletions
diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc
index 91d917c91..9e6eee1e8 100644
--- a/frontends/ast/Makefile.inc
+++ b/frontends/ast/Makefile.inc
@@ -3,4 +3,5 @@ OBJS += frontends/ast/ast.o
OBJS += frontends/ast/simplify.o
OBJS += frontends/ast/genrtlil.o
OBJS += frontends/ast/dpicall.o
+OBJS += frontends/ast/ast_binding.o
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 57552d86c..7be8ab565 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -52,8 +52,10 @@ namespace AST_INTERNAL {
const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL;
RTLIL::SigSpec ignoreThisSignalsInInitial;
AstNode *current_always, *current_top_block, *current_block, *current_block_child;
- AstModule *current_module;
+ Module *current_module;
bool current_always_clocked;
+ dict<std::string, int> current_memwr_count;
+ dict<std::string, pool<int>> current_memwr_visible;
}
// convert node types to string
@@ -175,6 +177,7 @@ std::string AST::type2str(AstNodeType type)
X(AST_STRUCT)
X(AST_UNION)
X(AST_STRUCT_ITEM)
+ X(AST_BIND)
#undef X
default:
log_abort();
@@ -196,7 +199,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id)
// create new node (AstNode constructor)
// (the optional child arguments make it easier to create AST trees)
-AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3)
+AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3, AstNode *child4)
{
static unsigned int hashidx_count = 123456789;
hashidx_count = mkhash_xorshift(hashidx_count);
@@ -233,6 +236,8 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
children.push_back(child2);
if (child3)
children.push_back(child3);
+ if (child4)
+ children.push_back(child4);
}
// create a (deep recursive) copy of a node
@@ -317,6 +322,8 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
fprintf(f, " reg");
if (is_signed)
fprintf(f, " signed");
+ if (is_unsized)
+ fprintf(f, " unsized");
if (basic_prep)
fprintf(f, " basic_prep");
if (lookahead)
@@ -847,7 +854,7 @@ RTLIL::Const AstNode::bitsAsConst(int width)
return bitsAsConst(width, is_signed);
}
-RTLIL::Const AstNode::asAttrConst()
+RTLIL::Const AstNode::asAttrConst() const
{
log_assert(type == AST_CONSTANT);
@@ -862,8 +869,17 @@ RTLIL::Const AstNode::asAttrConst()
return val;
}
-RTLIL::Const AstNode::asParaConst()
+RTLIL::Const AstNode::asParaConst() const
{
+ if (type == AST_REALVALUE)
+ {
+ AstNode *strnode = AstNode::mkconst_str(stringf("%f", realvalue));
+ RTLIL::Const val = strnode->asAttrConst();
+ val.flags |= RTLIL::CONST_FLAG_REAL;
+ delete strnode;
+ return val;
+ }
+
RTLIL::Const val = asAttrConst();
if (is_signed)
val.flags |= RTLIL::CONST_FLAG_SIGNED;
@@ -968,8 +984,15 @@ void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast)
obj->attributes[ID::src] = ast->loc_string();
}
-// create a new AstModule from an AST_MODULE AST node
-static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false)
+static bool param_has_no_default(const AstNode *param) {
+ const auto &children = param->children;
+ log_assert(param->type == AST_PARAMETER);
+ log_assert(children.size() <= 2);
+ return children.empty() ||
+ (children.size() == 1 && children[0]->type == AST_RANGE);
+}
+
+static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false)
{
log_assert(current_scope.empty());
log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE);
@@ -980,11 +1003,13 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str());
}
- current_module = new AstModule;
- current_module->ast = NULL;
- current_module->name = ast->str;
- set_src_attr(current_module, ast);
- current_module->set_bool_attribute(ID::cells_not_processed);
+ AstModule *module = new AstModule;
+ current_module = module;
+
+ module->ast = NULL;
+ module->name = ast->str;
+ set_src_attr(module, ast);
+ module->set_bool_attribute(ID::cells_not_processed);
current_ast_mod = ast;
AstNode *ast_before_simplify;
@@ -1006,6 +1031,10 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
if (!defer)
{
+ for (const AstNode *node : ast->children)
+ if (node->type == AST_PARAMETER && param_has_no_default(node))
+ log_file_error(node->filename, node->location.first_line, "Parameter `%s' has no default value and has not been overridden!\n", node->str.c_str());
+
bool blackbox_module = flag_lib;
if (!blackbox_module && !flag_noblackbox) {
@@ -1023,7 +1052,11 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
}
}
+ // simplify this module or interface using the current design as context
+ // for lookup up ports and wires within cells
+ set_simplify_design_context(design);
while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { }
+ set_simplify_design_context(nullptr);
if (flag_dump_ast2) {
log("Dumping AST after simplification:\n");
@@ -1120,7 +1153,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
- current_module->attributes[attr.first] = attr.second->asAttrConst();
+ module->attributes[attr.first] = attr.second->asAttrConst();
}
for (size_t i = 0; i < ast->children.size(); i++) {
AstNode *node = ast->children[i];
@@ -1148,35 +1181,93 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
continue;
- current_module->attributes[attr.first] = attr.second->asAttrConst();
+ module->attributes[attr.first] = attr.second->asAttrConst();
}
+ for (const AstNode *node : ast->children)
+ if (node->type == AST_PARAMETER)
+ current_module->avail_parameters(node->str);
}
if (ast->type == AST_INTERFACE)
- current_module->set_bool_attribute(ID::is_interface);
- current_module->ast = ast_before_simplify;
- current_module->nolatches = flag_nolatches;
- current_module->nomeminit = flag_nomeminit;
- current_module->nomem2reg = flag_nomem2reg;
- current_module->mem2reg = flag_mem2reg;
- current_module->noblackbox = flag_noblackbox;
- current_module->lib = flag_lib;
- current_module->nowb = flag_nowb;
- current_module->noopt = flag_noopt;
- current_module->icells = flag_icells;
- current_module->pwires = flag_pwires;
- current_module->autowire = flag_autowire;
- current_module->fixup_ports();
+ module->set_bool_attribute(ID::is_interface);
+ module->ast = ast_before_simplify;
+ module->nolatches = flag_nolatches;
+ module->nomeminit = flag_nomeminit;
+ module->nomem2reg = flag_nomem2reg;
+ module->mem2reg = flag_mem2reg;
+ module->noblackbox = flag_noblackbox;
+ module->lib = flag_lib;
+ module->nowb = flag_nowb;
+ module->noopt = flag_noopt;
+ module->icells = flag_icells;
+ module->pwires = flag_pwires;
+ module->autowire = flag_autowire;
+ module->fixup_ports();
if (flag_dump_rtlil) {
log("Dumping generated RTLIL:\n");
- log_module(current_module);
+ log_module(module);
log("--- END OF RTLIL DUMP ---\n");
}
+ design->add(current_module);
return current_module;
}
+RTLIL::Module *
+AST_INTERNAL::process_and_replace_module(RTLIL::Design *design,
+ RTLIL::Module *old_module,
+ AstNode *new_ast,
+ AstNode *original_ast)
+{
+ // The old module will be deleted. Rename and mark for deletion, using
+ // a static counter to make sure we get a unique name.
+ static unsigned counter;
+ std::ostringstream new_name;
+ new_name << old_module->name.str()
+ << "_before_process_and_replace_module_"
+ << counter;
+ ++counter;
+
+ design->rename(old_module, new_name.str());
+ old_module->set_bool_attribute(ID::to_delete);
+
+ // Check if the module was the top module. If it was, we need to remove
+ // the top attribute and put it on the new module.
+ bool is_top = false;
+ if (old_module->get_bool_attribute(ID::initial_top)) {
+ old_module->attributes.erase(ID::initial_top);
+ is_top = true;
+ }
+
+ // Generate RTLIL from AST for the new module and add to the design:
+ RTLIL::Module* new_module = process_module(design, new_ast, false, original_ast);
+
+ if (is_top)
+ new_module->set_bool_attribute(ID::top);
+
+ return new_module;
+}
+
+// renames identifiers in tasks and functions within a package
+static void rename_in_package_stmts(AstNode *pkg)
+{
+ std::unordered_set<std::string> idents;
+ for (AstNode *item : pkg->children)
+ idents.insert(item->str);
+ std::function<void(AstNode*)> rename =
+ [&rename, &idents, pkg](AstNode *node) {
+ for (AstNode *child : node->children) {
+ if (idents.count(child->str))
+ child->str = pkg->str + "::" + child->str.substr(1);
+ rename(child);
+ }
+ };
+ for (AstNode *item : pkg->children)
+ if (item->type == AST_FUNCTION || item->type == AST_TASK)
+ rename(item);
+}
+
// create AstModule instances for all modules in the AST tree and add them to 'design'
void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire)
@@ -1202,12 +1293,12 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
flag_autowire = autowire;
log_assert(current_ast->type == AST_DESIGN);
- for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++)
+ for (AstNode *child : current_ast->children)
{
- if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE)
+ if (child->type == AST_MODULE || child->type == AST_INTERFACE)
{
for (auto n : design->verilog_globals)
- (*it)->children.push_back(n->clone());
+ child->children.push_back(n->clone());
// append nodes from previous packages using package-qualified names
for (auto &n : design->verilog_packages) {
@@ -1222,45 +1313,63 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
} else {
cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1);
}
- (*it)->children.push_back(cloned_node);
+ child->children.push_back(cloned_node);
}
}
- if (flag_icells && (*it)->str.compare(0, 2, "\\$") == 0)
- (*it)->str = (*it)->str.substr(1);
+ if (flag_icells && child->str.compare(0, 2, "\\$") == 0)
+ child->str = child->str.substr(1);
- if (defer)
- (*it)->str = "$abstract" + (*it)->str;
+ bool defer_local = defer;
+ if (!defer_local)
+ for (const AstNode *node : child->children)
+ if (node->type == AST_PARAMETER && param_has_no_default(node))
+ {
+ log("Deferring `%s' because it contains parameter(s) without defaults.\n", child->str.c_str());
+ defer_local = true;
+ break;
+ }
- if (design->has((*it)->str)) {
- RTLIL::Module *existing_mod = design->module((*it)->str);
+
+ if (defer_local)
+ child->str = "$abstract" + child->str;
+
+ if (design->has(child->str)) {
+ RTLIL::Module *existing_mod = design->module(child->str);
if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) {
- log_file_error((*it)->filename, (*it)->location.first_line, "Re-definition of module `%s'!\n", (*it)->str.c_str());
+ log_file_error(child->filename, child->location.first_line, "Re-definition of module `%s'!\n", child->str.c_str());
} else if (nooverwrite) {
log("Ignoring re-definition of module `%s' at %s.\n",
- (*it)->str.c_str(), (*it)->loc_string().c_str());
+ child->str.c_str(), child->loc_string().c_str());
continue;
} else {
log("Replacing existing%s module `%s' at %s.\n",
existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "",
- (*it)->str.c_str(), (*it)->loc_string().c_str());
+ child->str.c_str(), child->loc_string().c_str());
design->remove(existing_mod);
}
}
- design->add(process_module(*it, defer));
+ process_module(design, child, defer_local);
current_ast_mod = nullptr;
}
- else if ((*it)->type == AST_PACKAGE) {
+ else if (child->type == AST_PACKAGE) {
// process enum/other declarations
- (*it)->simplify(true, false, false, 1, -1, false, false);
- design->verilog_packages.push_back((*it)->clone());
+ child->simplify(true, false, false, 1, -1, false, false);
+ rename_in_package_stmts(child);
+ design->verilog_packages.push_back(child->clone());
current_scope.clear();
}
+ else if (child->type == AST_BIND) {
+ // top-level bind construct
+ for (RTLIL::Binding *binding : child->genBindings())
+ design->add(binding);
+ }
else {
// must be global definition
- (*it)->simplify(false, false, false, 1, -1, false, false); //process enum/other declarations
- design->verilog_globals.push_back((*it)->clone());
+ if (child->type == AST_PARAMETER)
+ child->type = AST_LOCALPARAM; // cannot be overridden
+ design->verilog_globals.push_back(child->clone());
current_scope.clear();
}
}
@@ -1351,13 +1460,32 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule
}
}
+// AstModules may contain cells marked with ID::reprocess_after, which indicates
+// that it should be reprocessed once the specified module has been elaborated.
+bool AstModule::reprocess_if_necessary(RTLIL::Design *design)
+{
+ for (const RTLIL::Cell *cell : cells())
+ {
+ std::string modname = cell->get_string_attribute(ID::reprocess_after);
+ if (modname.empty())
+ continue;
+ if (design->module(modname) || design->module("$abstract" + modname)) {
+ log("Reprocessing module %s because instantiated module %s has become available.\n",
+ log_id(name), log_id(modname));
+ loadconfig();
+ process_and_replace_module(design, this, ast, NULL);
+ return true;
+ }
+ }
+ return false;
+}
+
// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again
// from AST. The interface members are copied into the AST module with the prefix of the interface.
-void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces)
+void AstModule::expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces)
{
loadconfig();
- bool is_top = false;
AstNode *new_ast = ast->clone();
for (auto &intf : local_interfaces) {
std::string intfname = intf.first.str();
@@ -1414,29 +1542,15 @@ void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdStri
}
}
- // The old module will be deleted. Rename and mark for deletion:
- std::string original_name = this->name.str();
- std::string changed_name = original_name + "_before_replacing_local_interfaces";
- design->rename(this, changed_name);
- this->set_bool_attribute(ID::to_delete);
+ // Generate RTLIL from AST for the new module and add to the design,
+ // renaming this module to move it out of the way.
+ RTLIL::Module* new_module =
+ process_and_replace_module(design, this, new_ast, ast_before_replacing_interface_ports);
- // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the
- // new module.
- if (this->get_bool_attribute(ID::initial_top)) {
- this->attributes.erase(ID::initial_top);
- is_top = true;
- }
-
- // Generate RTLIL from AST for the new module and add to the design:
- AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports);
- delete(new_ast);
- design->add(newmod);
- RTLIL::Module* mod = design->module(original_name);
- if (is_top)
- mod->set_bool_attribute(ID::top);
+ delete new_ast;
// Set the attribute "interfaces_replaced_in_module" so that it does not happen again.
- mod->set_bool_attribute(ID::interfaces_replaced_in_module);
+ new_module->set_bool_attribute(ID::interfaces_replaced_in_module);
}
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
@@ -1486,7 +1600,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr
explode_interface_port(new_ast, intfmodule, intfname, modport);
}
- design->add(process_module(new_ast, false));
+ process_module(design, new_ast, false);
design->module(modname)->check();
RTLIL::Module* mod = design->module(modname);
@@ -1537,7 +1651,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr
if (!design->has(modname)) {
new_ast->str = modname;
- design->add(process_module(new_ast, false, NULL, quiet));
+ process_module(design, new_ast, false, NULL, quiet);
design->module(modname)->check();
} else if (!quiet) {
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
@@ -1547,6 +1661,40 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict<RTLIL::IdStr
return modname;
}
+static std::string serialize_param_value(const RTLIL::Const &val) {
+ std::string res;
+ if (val.flags & RTLIL::ConstFlags::CONST_FLAG_STRING)
+ res.push_back('t');
+ if (val.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED)
+ res.push_back('s');
+ if (val.flags & RTLIL::ConstFlags::CONST_FLAG_REAL)
+ res.push_back('r');
+ res += stringf("%d", GetSize(val));
+ res.push_back('\'');
+ for (int i = GetSize(val) - 1; i >= 0; i--) {
+ switch (val.bits[i]) {
+ case RTLIL::State::S0: res.push_back('0'); break;
+ case RTLIL::State::S1: res.push_back('1'); break;
+ case RTLIL::State::Sx: res.push_back('x'); break;
+ case RTLIL::State::Sz: res.push_back('z'); break;
+ case RTLIL::State::Sa: res.push_back('?'); break;
+ case RTLIL::State::Sm: res.push_back('m'); break;
+ }
+ }
+ return res;
+}
+
+std::string AST::derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters) {
+ std::string para_info;
+ for (const auto &elem : parameters)
+ para_info += stringf("%s=%s", elem.first.c_str(), serialize_param_value(elem.second).c_str());
+
+ if (para_info.size() > 60)
+ return "$paramod$" + sha1(para_info) + stripped_name;
+ else
+ return "$paramod" + stripped_name + para_info;
+}
+
// create a new parametric module (when needed) and return the name of the generated module
std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, AstNode **new_ast_out, bool quiet)
{
@@ -1555,9 +1703,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
if (stripped_name.compare(0, 9, "$abstract") == 0)
stripped_name = stripped_name.substr(9);
- std::string para_info;
-
int para_counter = 0;
+ std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters;
for (const auto child : ast->children) {
if (child->type != AST_PARAMETER)
continue;
@@ -1566,25 +1713,21 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
if (it != parameters.end()) {
if (!quiet)
log("Parameter %s = %s\n", child->str.c_str(), log_signal(it->second));
- para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second));
+ named_parameters.emplace_back(child->str, it->second);
continue;
}
it = parameters.find(stringf("$%d", para_counter));
if (it != parameters.end()) {
if (!quiet)
log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(it->second));
- para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second));
+ named_parameters.emplace_back(child->str, it->second);
continue;
}
}
- std::string modname;
- if (parameters.size() == 0)
- modname = stripped_name;
- else if (para_info.size() > 60)
- modname = "$paramod$" + sha1(para_info) + stripped_name;
- else
- modname = "$paramod" + stripped_name + para_info;
+ std::string modname = stripped_name;
+ if (parameters.size()) // not named_parameters to cover hierarchical defparams
+ modname = derived_module_name(stripped_name, named_parameters);
if (design->has(modname))
return modname;
@@ -1619,6 +1762,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
}
continue;
rewrite_parameter:
+ if (param_has_no_default(child))
+ child->children.insert(child->children.begin(), nullptr);
delete child->children.at(0);
if ((it->second.flags & RTLIL::CONST_FLAG_REAL) != 0) {
child->children[0] = new AstNode(AST_REALVALUE);
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 1c9a6ee47..48ec9a063 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -1,7 +1,7 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -160,7 +160,8 @@ namespace AST
AST_TYPEDEF,
AST_STRUCT,
AST_UNION,
- AST_STRUCT_ITEM
+ AST_STRUCT_ITEM,
+ AST_BIND
};
struct AstSrcLocType {
@@ -220,7 +221,7 @@ namespace AST
AstSrcLocType location;
// creating and deleting nodes
- AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL);
+ AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr);
AstNode *clone() const;
void cloneInto(AstNode *other) const;
void delete_children();
@@ -261,6 +262,7 @@ namespace AST
void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes);
void meminfo(int &mem_width, int &mem_size, int &addr_bits);
bool detect_latch(const std::string &var);
+ const RTLIL::Module* lookup_cell_module();
// additional functionality for evaluating constant functions
struct varinfo_t {
@@ -283,6 +285,9 @@ namespace AST
void dumpAst(FILE *f, std::string indent) const;
void dumpVlog(FILE *f, std::string indent) const;
+ // Generate RTLIL for a bind construct
+ std::vector<RTLIL::Binding *> genBindings() const;
+
// used by genRTLIL() for detecting expression width and sign
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL);
@@ -291,7 +296,7 @@ namespace AST
// for expressions the resulting signal vector is returned
// all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module
RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false);
- RTLIL::SigSpec genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL);
+ RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL);
// compare AST nodes
bool operator==(const AstNode &other) const;
@@ -309,8 +314,8 @@ namespace AST
RTLIL::Const bitsAsConst(int width, bool is_signed);
RTLIL::Const bitsAsConst(int width = -1);
RTLIL::Const bitsAsUnsizedConst(int width);
- RTLIL::Const asAttrConst();
- RTLIL::Const asParaConst();
+ RTLIL::Const asAttrConst() const;
+ RTLIL::Const asParaConst() const;
uint64_t asInt(bool is_signed);
bool bits_only_01() const;
bool asBool() const;
@@ -326,6 +331,9 @@ namespace AST
// helpers for locations
std::string loc_string() const;
+
+ // Helper for looking up identifiers which are prefixed with the current module name
+ std::string try_pop_module_prefix() const;
};
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
@@ -341,7 +349,8 @@ namespace AST
RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool mayfail) override;
RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail) override;
std::string derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, AstNode **new_ast_out, bool quiet = false);
- void reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override;
+ void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override;
+ bool reprocess_if_necessary(RTLIL::Design *design) override;
RTLIL::Module *clone() const override;
void loadconfig() const;
};
@@ -367,6 +376,17 @@ namespace AST
// Helper for setting the src attribute.
void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);
+
+ // struct helper exposed from simplify for genrtlil
+ AstNode *make_struct_member_range(AstNode *node, AstNode *member_node);
+
+ // generate standard $paramod... derived module name; parameters should be
+ // in the order they are declared in the instantiated module
+ std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters);
+
+ // used to provide simplify() access to the current design for looking up
+ // modules, ports, wires, etc.
+ void set_simplify_design_context(const RTLIL::Design *design);
}
namespace AST_INTERNAL
@@ -379,10 +399,24 @@ namespace AST_INTERNAL
extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr;
extern RTLIL::SigSpec ignoreThisSignalsInInitial;
extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child;
- extern AST::AstModule *current_module;
+ extern RTLIL::Module *current_module;
extern bool current_always_clocked;
+ extern dict<std::string, int> current_memwr_count;
+ extern dict<std::string, pool<int>> current_memwr_visible;
struct LookaheadRewriter;
struct ProcessGenerator;
+
+ // Create and add a new AstModule from new_ast, then use it to replace
+ // old_module in design, renaming old_module to move it out of the way.
+ // Return the new module.
+ //
+ // If original_ast is not null, it will be used as the AST node for the
+ // new module. Otherwise, new_ast will be used.
+ RTLIL::Module *
+ process_and_replace_module(RTLIL::Design *design,
+ RTLIL::Module *old_module,
+ AST::AstNode *new_ast,
+ AST::AstNode *original_ast = nullptr);
}
YOSYS_NAMESPACE_END
diff --git a/frontends/ast/ast_binding.cc b/frontends/ast/ast_binding.cc
new file mode 100644
index 000000000..c20d1df4d
--- /dev/null
+++ b/frontends/ast/ast_binding.cc
@@ -0,0 +1,49 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ast_binding.h"
+#include "ast.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+using namespace AST_INTERNAL;
+
+AST::Binding::Binding(RTLIL::IdString target_type,
+ RTLIL::IdString target_name,
+ const AstNode &cell)
+ : RTLIL::Binding(target_type, target_name),
+ ast_node(cell.clone())
+{
+ log_assert(cell.type == AST_CELL);
+}
+
+std::string
+AST::Binding::describe() const
+{
+ std::ostringstream oss;
+ oss << "directive to bind " << ast_node->str
+ << " to " << target_name.str();
+ if (!target_type.empty())
+ oss << " (target type: "
+ << target_type.str()
+ << ")";
+ return oss.str();
+}
+
+PRIVATE_NAMESPACE_END
diff --git a/frontends/ast/ast_binding.h b/frontends/ast/ast_binding.h
new file mode 100644
index 000000000..641497d52
--- /dev/null
+++ b/frontends/ast/ast_binding.h
@@ -0,0 +1,58 @@
+/* -*- c++ -*-
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * ---
+ *
+ * This header declares the AST::Binding class
+ *
+ * This is used to support the bind directive and is to RTLIL::Binding as
+ * AST::AstModule is to RTLIL::Module, holding a syntax-level representation of
+ * cells until we get to a stage where they make sense. In the case of a bind
+ * directive, this is when we elaborate the design in the hierarchy pass.
+ *
+ */
+
+#ifndef AST_BINDING_H
+#define AST_BINDING_H
+
+#include "kernel/rtlil.h"
+#include "kernel/binding.h"
+
+#include <memory>
+
+YOSYS_NAMESPACE_BEGIN
+
+namespace AST
+{
+ class Binding : public RTLIL::Binding
+ {
+ public:
+ Binding(RTLIL::IdString target_type,
+ RTLIL::IdString target_name,
+ const AstNode &cell);
+
+ std::string describe() const override;
+
+ private:
+ // The syntax-level representation of the cell to be bound.
+ std::unique_ptr<AstNode> ast_node;
+ };
+}
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc
index 948c9083c..12a7e1183 100644
--- a/frontends/ast/dpicall.cc
+++ b/frontends/ast/dpicall.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index d4299bf69..4c25287ad 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -28,8 +28,10 @@
#include "kernel/log.h"
#include "kernel/utils.h"
+#include "kernel/binding.h"
#include "libs/sha1/sha1.h"
#include "ast.h"
+#include "ast_binding.h"
#include <sstream>
#include <stdarg.h>
@@ -319,16 +321,14 @@ struct AST_INTERNAL::ProcessGenerator
LookaheadRewriter la_rewriter(always);
// generate process and simple root case
- proc = new RTLIL::Process;
+ proc = current_module->addProcess(stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++));
set_src_attr(proc, always);
- proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++);
for (auto &attr : always->attributes) {
if (attr.second->type != AST_CONSTANT)
log_file_error(always->filename, always->location.first_line, "Attribute `%s' with non-constant value!\n",
attr.first.c_str());
proc->attributes[attr.first] = attr.second->asAttrConst();
}
- current_module->processes[proc->name] = proc;
current_case = &proc->root_case;
// create initial temporary signal for all output registers
@@ -399,6 +399,9 @@ struct AST_INTERNAL::ProcessGenerator
if (child->type == AST_BLOCK)
processAst(child);
+ for (auto sync: proc->syncs)
+ processMemWrites(sync);
+
if (initSyncSignals.size() > 0)
{
RTLIL::SyncRule *sync = new RTLIL::SyncRule;
@@ -563,7 +566,7 @@ struct AST_INTERNAL::ProcessGenerator
case AST_ASSIGN_LE:
{
RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue;
- RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap());
+ RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), true, &subst_rvalue_map.stdmap());
pool<SigBit> lvalue_sigbits;
for (int i = 0; i < GetSize(lvalue); i++) {
@@ -590,9 +593,13 @@ struct AST_INTERNAL::ProcessGenerator
case AST_CASE:
{
+ int width_hint;
+ bool sign_hint;
+ ast->detectSignWidth(width_hint, sign_hint);
+
RTLIL::SwitchRule *sw = new RTLIL::SwitchRule;
set_src_attr(sw, ast);
- sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap());
+ sw->signal = ast->children[0]->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap());
current_case->switches.push_back(sw);
for (auto &attr : ast->attributes) {
@@ -634,7 +641,7 @@ struct AST_INTERNAL::ProcessGenerator
else if (node->type == AST_BLOCK)
processAst(node);
else
- current_case->compare.push_back(node->genWidthRTLIL(sw->signal.size(), &subst_rvalue_map.stdmap()));
+ current_case->compare.push_back(node->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap()));
}
if (default_case != current_case)
sw->cases.push_back(current_case);
@@ -698,8 +705,99 @@ struct AST_INTERNAL::ProcessGenerator
log_abort();
}
}
+
+ void processMemWrites(RTLIL::SyncRule *sync)
+ {
+ // Maps per-memid AST_MEMWR IDs to indices in the mem_write_actions array.
+ dict<std::pair<std::string, int>, int> port_map;
+ for (auto child : always->children)
+ if (child->type == AST_MEMWR)
+ {
+ std::string memid = child->str;
+ int portid = child->children[3]->asInt(false);
+ int cur_idx = GetSize(sync->mem_write_actions);
+ RTLIL::MemWriteAction action;
+ set_src_attr(&action, child);
+ action.memid = memid;
+ action.address = child->children[0]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap());
+ action.data = child->children[1]->genWidthRTLIL(current_module->memories[memid]->width, true, &subst_rvalue_map.stdmap());
+ action.enable = child->children[2]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap());
+ RTLIL::Const orig_priority_mask = child->children[4]->bitsAsConst();
+ RTLIL::Const priority_mask = RTLIL::Const(0, cur_idx);
+ for (int i = 0; i < portid; i++) {
+ int new_bit = port_map[std::make_pair(memid, i)];
+ priority_mask.bits[new_bit] = orig_priority_mask.bits[i];
+ }
+ action.priority_mask = priority_mask;
+ sync->mem_write_actions.push_back(action);
+ port_map[std::make_pair(memid, portid)] = cur_idx;
+ }
+ }
};
+// Generate RTLIL for a bind construct
+//
+// The AST node will have one or more AST_IDENTIFIER children, which were added
+// by bind_target_instance in the parser. After these, it will have one or more
+// cells, as parsed by single_cell. These have type AST_CELL.
+//
+// If there is more than one AST_IDENTIFIER, the first one should be considered
+// a module identifier. If there is only one AST_IDENTIFIER, we can't tell at
+// this point whether it's a module/interface name or the name of an instance
+// because the correct interpretation depends on what's visible at elaboration
+// time. For now, we just treat it as a target instance with unknown type, and
+// we'll deal with the corner case in the hierarchy pass.
+//
+// To simplify downstream code, RTLIL::Binding only has a single target and
+// single bound instance. If we see the syntax that allows more than one of
+// either, we split it into multiple Binding objects.
+std::vector<RTLIL::Binding *> AstNode::genBindings() const
+{
+ // Partition children into identifiers and cells
+ int num_ids = 0;
+ for (int i = 0; i < GetSize(children); ++i) {
+ if (children[i]->type != AST_IDENTIFIER) {
+ log_assert(i > 0);
+ num_ids = i;
+ break;
+ }
+ }
+
+ // We should have found at least one child that's not an identifier
+ log_assert(num_ids > 0);
+
+ // Make sense of the identifiers, extracting a possible type name and a
+ // list of hierarchical IDs. We represent an unknown type with an empty
+ // string.
+ RTLIL::IdString tgt_type;
+ int first_tgt_inst = 0;
+ if (num_ids > 1) {
+ tgt_type = children[0]->str;
+ first_tgt_inst = 1;
+ }
+
+ std::vector<RTLIL::Binding *> ret;
+
+ // At this point, we know that children with index >= first_tgt_inst and
+ // index < num_ids are (hierarchical?) names of target instances. Make a
+ // binding object for each of them, and fill in the generated instance
+ // cells each time.
+ for (int i = first_tgt_inst; i < num_ids; ++i) {
+ const AstNode &tgt_child = *children[i];
+
+ for (int j = num_ids; j < GetSize(children); ++j) {
+ const AstNode &cell_child = *children[j];
+
+ log_assert(cell_child.type == AST_CELL);
+
+ ret.push_back(new AST::Binding(tgt_type, tgt_child.str,
+ cell_child));
+ }
+ }
+
+ return ret;
+}
+
// detect sign and width of an expression
void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real)
{
@@ -732,8 +830,15 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
case AST_IDENTIFIER:
id_ast = id2ast;
- if (id_ast == NULL && current_scope.count(str))
- id_ast = current_scope.at(str);
+ if (!id_ast) {
+ if (current_scope.count(str))
+ id_ast = current_scope[str];
+ else {
+ std::string alt = try_pop_module_prefix();
+ if (current_scope.count(alt))
+ id_ast = current_scope[alt];
+ }
+ }
if (!id_ast)
log_file_error(filename, location.first_line, "Failed to resolve identifier %s for width detection!\n", str.c_str());
if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) {
@@ -772,6 +877,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
if (children.size() > 1)
range = children[1];
+ } else if (id_ast->type == AST_STRUCT_ITEM) {
+ AstNode *tmp_range = make_struct_member_range(this, id_ast);
+ this_width = tmp_range->range_left - tmp_range->range_right + 1;
+ delete tmp_range;
} else
log_file_error(filename, location.first_line, "Failed to detect width for identifier %s!\n", str.c_str());
if (range) {
@@ -823,7 +932,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (children.at(0)->type != AST_CONSTANT)
log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");
children.at(1)->detectSignWidthWorker(width_hint, sign_hint);
- width_hint = children.at(0)->bitsAsConst().as_int();
+ this_width = children.at(0)->bitsAsConst().as_int();
+ width_hint = max(width_hint, this_width);
if (width_hint <= 0)
log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");
break;
@@ -923,6 +1033,40 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
width_hint = max(width_hint, this_width);
break;
+ case AST_CASE:
+ {
+ // This detects the _overall_ sign and width to be used for comparing
+ // the case expression with the case item expressions. The case
+ // expression and case item expressions are extended to the maximum
+ // width among them, and are only interpreted as signed if all of them
+ // are signed.
+ width_hint = -1;
+ sign_hint = true;
+ auto visit_case_expr = [&width_hint, &sign_hint] (AstNode *node) {
+ int sub_width_hint = -1;
+ bool sub_sign_hint = true;
+ node->detectSignWidth(sub_width_hint, sub_sign_hint);
+ width_hint = max(width_hint, sub_width_hint);
+ sign_hint &= sub_sign_hint;
+ };
+ visit_case_expr(children[0]);
+ for (size_t i = 1; i < children.size(); i++) {
+ AstNode *child = children[i];
+ for (AstNode *v : child->children)
+ if (v->type != AST_DEFAULT && v->type != AST_BLOCK)
+ visit_case_expr(v);
+ }
+ break;
+ }
+
+ case AST_PREFIX:
+ // Prefix nodes always resolve to identifiers in generate loops, so we
+ // can simply perform the resolution to determine the sign and width.
+ simplify(true, false, false, 1, -1, false, false);
+ log_assert(type == AST_IDENTIFIER);
+ detectSignWidthWorker(width_hint, sign_hint, found_real);
+ break;
+
case AST_FCALL:
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
if (GetSize(children) == 1) {
@@ -944,6 +1088,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
}
break;
}
+ if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") {
+ width_hint = 32;
+ sign_hint = true;
+ break;
+ }
if (current_scope.count(str))
{
// This width detection is needed for function calls which are
@@ -983,8 +1132,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
// everything should have been handled above -> print error if not.
default:
+ AstNode *current_scope_ast = current_ast_mod == nullptr ? current_ast : current_ast_mod;
for (auto f : log_files)
- current_ast_mod->dumpAst(f, "verilog-ast> ");
+ current_scope_ast->dumpAst(f, "verilog-ast> ");
log_file_error(filename, location.first_line, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
}
@@ -1000,6 +1150,12 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real
if (found_real)
*found_real = false;
detectSignWidthWorker(width_hint, sign_hint, found_real);
+
+ constexpr int kWidthLimit = 1 << 24;
+ if (width_hint >= kWidthLimit)
+ log_file_error(filename, location.first_line,
+ "Expression width %d exceeds implementation limit of %d!\n",
+ width_hint, kWidthLimit);
}
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
@@ -1227,7 +1383,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
RTLIL::Wire *wire = current_module->addWire(str);
set_src_attr(wire, this);
wire->name = str;
- if (flag_autowire)
+
+ // If we are currently processing a bind directive which wires up
+ // signals or parameters explicitly, rather than with .*, then
+ // current_module will start out empty and we don't want to warn the
+ // user about it: we'll spot broken wiring later, when we run the
+ // hierarchy pass.
+ if (dynamic_cast<RTLIL::Binding*>(current_module)) {
+ /* nothing to do here */
+ } else if (flag_autowire)
log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str());
else
log_file_error(filename, location.first_line, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
@@ -1367,13 +1531,20 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// changing the size of signal can be done directly using RTLIL::SigSpec
case AST_CAST_SIZE: {
RTLIL::SigSpec size = children[0]->genRTLIL();
- RTLIL::SigSpec sig = children[1]->genRTLIL();
if (!size.is_fully_const())
log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");
int width = size.as_int();
if (width <= 0)
log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");
- sig.extend_u0(width, sign_hint);
+ // determine the *signedness* of the expression
+ int sub_width_hint = -1;
+ bool sub_sign_hint = true;
+ children[1]->detectSignWidth(sub_width_hint, sub_sign_hint);
+ // generate the signal given the *cast's* size and the
+ // *expression's* signedness
+ RTLIL::SigSpec sig = children[1]->genWidthRTLIL(width, sub_sign_hint);
+ // context may effect this node's signedness, but not that of the
+ // casted expression
is_signed = sign_hint;
return sig;
}
@@ -1638,43 +1809,35 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
return RTLIL::SigSpec(wire);
}
- // generate $memwr cells for memory write ports
- case AST_MEMWR:
+ // generate $meminit cells
case AST_MEMINIT:
{
std::stringstream sstr;
- sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
+ sstr << "$meminit$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
+
+ SigSpec en_sig = children[2]->genRTLIL();
- RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? ID($memwr) : ID($meminit));
+ RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($meminit_v2));
set_src_attr(cell, this);
int mem_width, mem_size, addr_bits;
id2ast->meminfo(mem_width, mem_size, addr_bits);
- int num_words = 1;
- if (type == AST_MEMINIT) {
- if (children[2]->type != AST_CONSTANT)
- log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n");
- num_words = int(children[2]->asInt(false));
- cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
- }
+ if (children[3]->type != AST_CONSTANT)
+ log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n");
+ int num_words = int(children[3]->asInt(false));
+ cell->parameters[ID::WORDS] = RTLIL::Const(num_words);
SigSpec addr_sig = children[0]->genRTLIL();
cell->setPort(ID::ADDR, addr_sig);
- cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words));
+ cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words, true));
+ cell->setPort(ID::EN, en_sig);
cell->parameters[ID::MEMID] = RTLIL::Const(str);
cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig));
cell->parameters[ID::WIDTH] = RTLIL::Const(current_module->memories[str]->width);
- if (type == AST_MEMWR) {
- cell->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::Sx, 1));
- cell->setPort(ID::EN, children[2]->genRTLIL());
- cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(0);
- cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(0);
- }
-
cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1);
}
break;
@@ -1728,7 +1891,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_ASSIGN:
{
RTLIL::SigSpec left = children[0]->genRTLIL();
- RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size());
+ RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size(), true);
if (left.has_const()) {
RTLIL::SigSpec new_left, new_right;
for (int i = 0; i < GetSize(left); i++)
@@ -1768,21 +1931,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
continue;
}
if (child->type == AST_PARASET) {
- int extra_const_flags = 0;
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
- if (child->children[0]->type == AST_REALVALUE) {
+ const AstNode *value = child->children[0];
+ if (value->type == AST_REALVALUE)
log_file_warning(filename, location.first_line, "Replacing floating point parameter %s.%s = %f with string.\n",
- log_id(cell), log_id(paraname), child->children[0]->realvalue);
- extra_const_flags = RTLIL::CONST_FLAG_REAL;
- auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
- strnode->cloneInto(child->children[0]);
- delete strnode;
- }
- if (child->children[0]->type != AST_CONSTANT)
+ log_id(cell), log_id(paraname), value->realvalue);
+ else if (value->type != AST_CONSTANT)
log_file_error(filename, location.first_line, "Parameter %s.%s with non-constant value!\n",
log_id(cell), log_id(paraname));
- cell->parameters[paraname] = child->children[0]->asParaConst();
- cell->parameters[paraname].flags |= extra_const_flags;
+ cell->parameters[paraname] = value->asParaConst();
continue;
}
if (child->type == AST_ARGUMENT) {
@@ -1799,7 +1956,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (sig.is_wire()) {
// if the resulting SigSpec is a wire, its
// signedness should match that of the AstNode
- log_assert(arg->is_signed == sig.as_wire()->is_signed);
+ if (arg->type == AST_IDENTIFIER && arg->id2ast && arg->id2ast->is_signed && !arg->is_signed)
+ // fully-sliced signed wire will be resolved
+ // once the module becomes available
+ log_assert(attributes.count(ID::reprocess_after));
+ else
+ log_assert(arg->is_signed == sig.as_wire()->is_signed);
} else if (arg->is_signed) {
// non-trivial signed nodes are indirected through
// signed wires to enable sign extension
@@ -1898,6 +2060,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
} break;
+ case AST_BIND: {
+ // Read a bind construct. This should have one or more cells as children.
+ for (RTLIL::Binding *binding : genBindings())
+ current_module->add(binding);
+ break;
+ }
+
case AST_FCALL: {
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq")
{
@@ -1943,8 +2112,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
default:
for (auto f : log_files)
current_ast_mod->dumpAst(f, "verilog-ast> ");
- type_name = type2str(type);
- log_file_error(filename, location.first_line, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str());
+ log_file_error(filename, location.first_line, "Don't know how to generate RTLIL code for %s node!\n", type2str(type).c_str());
}
return RTLIL::SigSpec();
@@ -1953,14 +2121,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or
// signals must be substituted before being used as input values (used by ProcessGenerator)
// note that this is using some global variables to communicate this special settings to AstNode::genRTLIL().
-RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr)
+RTLIL::SigSpec AstNode::genWidthRTLIL(int width, bool sgn, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr)
{
const dict<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr;
if (new_subst_ptr)
genRTLIL_subst_ptr = new_subst_ptr;
- bool sign_hint = true;
+ bool sign_hint = sgn;
int width_hint = width;
detectSignWidthWorker(width_hint, sign_hint);
RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint);
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 5c4dd290f..18b1e1e11 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -192,6 +192,7 @@ void AstNode::annotateTypedEnums(AstNode *template_node)
log_assert(current_scope.count(enum_type) == 1);
AstNode *enum_node = current_scope.at(enum_type);
log_assert(enum_node->type == AST_ENUM);
+ while (enum_node->simplify(true, false, false, 1, -1, false, true)) { }
//get width from 1st enum item:
log_assert(enum_node->children.size() >= 1);
AstNode *enum_item0 = enum_node->children[0];
@@ -330,6 +331,8 @@ static int size_packed_struct(AstNode *snode, int base_offset)
}
}
// range nodes are now redundant
+ for (AstNode *child : node->children)
+ delete child;
node->children.clear();
}
else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) {
@@ -344,6 +347,8 @@ static int size_packed_struct(AstNode *snode, int base_offset)
save_struct_array_width(node, width);
width *= array_count;
// range nodes are now redundant
+ for (AstNode *child : node->children)
+ delete child;
node->children.clear();
}
else if (node->range_left < 0) {
@@ -451,7 +456,7 @@ static AstNode *slice_range(AstNode *rnode, AstNode *snode)
}
-static AstNode *make_struct_member_range(AstNode *node, AstNode *member_node)
+AstNode *AST::make_struct_member_range(AstNode *node, AstNode *member_node)
{
// Work out the range in the packed array that corresponds to a struct member
// taking into account any range operations applicable to the current node
@@ -559,6 +564,227 @@ static std::string prefix_id(const std::string &prefix, const std::string &str)
return prefix + str;
}
+// direct access to this global should be limited to the following two functions
+static const RTLIL::Design *simplify_design_context = nullptr;
+
+void AST::set_simplify_design_context(const RTLIL::Design *design)
+{
+ log_assert(!simplify_design_context || !design);
+ simplify_design_context = design;
+}
+
+// lookup the module with the given name in the current design context
+static const RTLIL::Module* lookup_module(const std::string &name)
+{
+ return simplify_design_context->module(name);
+}
+
+const RTLIL::Module* AstNode::lookup_cell_module()
+{
+ log_assert(type == AST_CELL);
+
+ auto reprocess_after = [this] (const std::string &modname) {
+ if (!attributes.count(ID::reprocess_after))
+ attributes[ID::reprocess_after] = AstNode::mkconst_str(modname);
+ };
+
+ const AstNode *celltype = nullptr;
+ for (const AstNode *child : children)
+ if (child->type == AST_CELLTYPE) {
+ celltype = child;
+ break;
+ }
+ log_assert(celltype != nullptr);
+
+ const RTLIL::Module *module = lookup_module(celltype->str);
+ if (!module)
+ module = lookup_module("$abstract" + celltype->str);
+ if (!module) {
+ if (celltype->str.at(0) != '$')
+ reprocess_after(celltype->str);
+ return nullptr;
+ }
+
+ // build a mapping from true param name to param value
+ size_t para_counter = 0;
+ dict<RTLIL::IdString, RTLIL::Const> cell_params_map;
+ for (AstNode *child : children) {
+ if (child->type != AST_PARASET)
+ continue;
+
+ if (child->str.empty() && para_counter >= module->avail_parameters.size())
+ return nullptr; // let hierarchy handle this error
+ IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str;
+
+ const AstNode *value = child->children[0];
+ if (value->type != AST_REALVALUE && value->type != AST_CONSTANT)
+ return nullptr; // let genrtlil handle this error
+ cell_params_map[paraname] = value->asParaConst();
+ }
+
+ // put the parameters in order and generate the derived module name
+ std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters;
+ for (RTLIL::IdString param : module->avail_parameters) {
+ auto it = cell_params_map.find(param);
+ if (it != cell_params_map.end())
+ named_parameters.emplace_back(it->first, it->second);
+ }
+ std::string modname = celltype->str;
+ if (cell_params_map.size()) // not named_parameters to cover hierarchical defparams
+ modname = derived_module_name(celltype->str, named_parameters);
+
+ // try to find the resolved module
+ module = lookup_module(modname);
+ if (!module) {
+ reprocess_after(modname);
+ return nullptr;
+ }
+ return module;
+}
+
+// returns whether an expression contains an unbased unsized literal; does not
+// check the literal exists in a self-determined context
+static bool contains_unbased_unsized(const AstNode *node)
+{
+ if (node->type == AST_CONSTANT)
+ return node->is_unsized;
+ for (const AstNode *child : node->children)
+ if (contains_unbased_unsized(child))
+ return true;
+ return false;
+}
+
+// adds a wire to the current module with the given name that matches the
+// dimensions of the given wire reference
+void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str)
+{
+ AstNode *left = AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true);
+ AstNode *right = AstNode::mkconst_int(ref->start_offset, true);
+ if (ref->upto)
+ std::swap(left, right);
+ AstNode *range = new AstNode(AST_RANGE, left, right);
+
+ AstNode *wire = new AstNode(AST_WIRE, range);
+ wire->is_signed = ref->is_signed;
+ wire->is_logic = true;
+ wire->str = str;
+
+ current_ast_mod->children.push_back(wire);
+ current_scope[str] = wire;
+}
+
+enum class IdentUsage {
+ NotReferenced, // target variable is neither read or written in the block
+ Assigned, // target variable is always assigned before use
+ SyncRequired, // target variable may be used before it has been assigned
+};
+
+// determines whether a local variable a block is always assigned before it is
+// used, meaning the nosync attribute can automatically be added to that
+// variable
+static IdentUsage always_asgn_before_use(const AstNode *node, const std::string &target)
+{
+ // This variable has been referenced before it has necessarily been assigned
+ // a value in this procedure.
+ if (node->type == AST_IDENTIFIER && node->str == target)
+ return IdentUsage::SyncRequired;
+
+ // For case statements (which are also used for if/else), we check each
+ // possible branch. If the variable is assigned in all branches, then it is
+ // assigned, and a sync isn't required. If it used before assignment in any
+ // branch, then a sync is required.
+ if (node->type == AST_CASE) {
+ bool all_defined = true;
+ bool any_used = false;
+ bool has_default = false;
+ for (const AstNode *child : node->children) {
+ if (child->type == AST_COND && child->children.at(0)->type == AST_DEFAULT)
+ has_default = true;
+ IdentUsage nested = always_asgn_before_use(child, target);
+ if (nested != IdentUsage::Assigned && child->type == AST_COND)
+ all_defined = false;
+ if (nested == IdentUsage::SyncRequired)
+ any_used = true;
+ }
+ if (any_used)
+ return IdentUsage::SyncRequired;
+ else if (all_defined && has_default)
+ return IdentUsage::Assigned;
+ else
+ return IdentUsage::NotReferenced;
+ }
+
+ // Check if this is an assignment to the target variable. For simplicity, we
+ // don't analyze sub-ranges of the variable.
+ if (node->type == AST_ASSIGN_EQ) {
+ const AstNode *ident = node->children.at(0);
+ if (ident->type == AST_IDENTIFIER && ident->str == target)
+ return IdentUsage::Assigned;
+ }
+
+ for (const AstNode *child : node->children) {
+ IdentUsage nested = always_asgn_before_use(child, target);
+ if (nested != IdentUsage::NotReferenced)
+ return nested;
+ }
+ return IdentUsage::NotReferenced;
+}
+
+static const std::string auto_nosync_prefix = "\\AutoNosync";
+
+// mark a local variable in an always_comb block for automatic nosync
+// consideration
+static void mark_auto_nosync(AstNode *block, const AstNode *wire)
+{
+ log_assert(block->type == AST_BLOCK);
+ log_assert(wire->type == AST_WIRE);
+ block->attributes[auto_nosync_prefix + wire->str] = AstNode::mkconst_int(1,
+ false);
+}
+
+// check a procedural block for auto-nosync markings, remove them, and add
+// nosync to local variables as necessary
+static void check_auto_nosync(AstNode *node)
+{
+ std::vector<RTLIL::IdString> attrs_to_drop;
+ for (const auto& elem : node->attributes) {
+ // skip attributes that don't begin with the prefix
+ if (elem.first.compare(0, auto_nosync_prefix.size(),
+ auto_nosync_prefix.c_str()))
+ continue;
+
+ // delete and remove the attribute once we're done iterating
+ attrs_to_drop.push_back(elem.first);
+
+ // find the wire based on the attribute
+ std::string wire_name = elem.first.substr(auto_nosync_prefix.size());
+ auto it = current_scope.find(wire_name);
+ if (it == current_scope.end())
+ continue;
+
+ // analyze the usage of the local variable in this block
+ IdentUsage ident_usage = always_asgn_before_use(node, wire_name);
+ if (ident_usage != IdentUsage::Assigned)
+ continue;
+
+ // mark the wire with `nosync`
+ AstNode *wire = it->second;
+ log_assert(wire->type == AST_WIRE);
+ wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
+ }
+
+ // remove the attributes we've "consumed"
+ for (const RTLIL::IdString &str : attrs_to_drop) {
+ auto it = node->attributes.find(str);
+ delete it->second;
+ node->attributes.erase(it);
+ }
+
+ // check local variables in any nested blocks
+ for (AstNode *child : node->children)
+ check_auto_nosync(child);
+}
+
// convert the AST into a simpler AST that has all parameters substituted by their
// values, unrolled for-loops, expanded generate blocks, etc. when this function
// is done with an AST it can be converted into RTLIL using genRTLIL().
@@ -866,6 +1092,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
}
+
+ for (AstNode *child : children)
+ if (child->type == AST_ALWAYS &&
+ child->attributes.count(ID::always_comb))
+ check_auto_nosync(child);
}
// create name resolution entries for all objects with names
@@ -874,7 +1105,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[i];
// these nodes appear at the top level in a package and can define names
- if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF) {
+ if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF || node->type == AST_FUNCTION || node->type == AST_TASK) {
current_scope[node->str] = node;
}
if (node->type == AST_ENUM) {
@@ -915,6 +1146,113 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
+ if (type == AST_CELL) {
+ bool lookup_suggested = false;
+
+ for (AstNode *child : children) {
+ // simplify any parameters to constants
+ if (child->type == AST_PARASET)
+ while (child->simplify(true, false, false, 1, -1, false, true)) { }
+
+ // look for patterns which _may_ indicate ambiguity requiring
+ // resolution of the underlying module
+ if (child->type == AST_ARGUMENT) {
+ if (child->children.size() != 1)
+ continue;
+ const AstNode *value = child->children[0];
+ if (value->type == AST_IDENTIFIER) {
+ const AstNode *elem = value->id2ast;
+ if (elem == nullptr) {
+ if (current_scope.count(value->str))
+ elem = current_scope.at(value->str);
+ else
+ continue;
+ }
+ if (elem->type == AST_MEMORY)
+ // need to determine is the is a read or wire
+ lookup_suggested = true;
+ else if (elem->type == AST_WIRE && elem->is_signed && !value->children.empty())
+ // this may be a fully sliced signed wire which needs
+ // to be indirected to produce an unsigned connection
+ lookup_suggested = true;
+ }
+ else if (contains_unbased_unsized(value))
+ // unbased unsized literals extend to width of the context
+ lookup_suggested = true;
+ }
+ }
+
+ const RTLIL::Module *module = nullptr;
+ if (lookup_suggested)
+ module = lookup_cell_module();
+ if (module) {
+ size_t port_counter = 0;
+ for (AstNode *child : children) {
+ if (child->type != AST_ARGUMENT)
+ continue;
+
+ // determine the full name of port this argument is connected to
+ RTLIL::IdString port_name;
+ if (child->str.size())
+ port_name = child->str;
+ else {
+ if (port_counter >= module->ports.size())
+ log_file_error(filename, location.first_line,
+ "Cell instance has more ports than the module!\n");
+ port_name = module->ports[port_counter++];
+ }
+
+ // find the port's wire in the underlying module
+ const RTLIL::Wire *ref = module->wire(port_name);
+ if (ref == nullptr)
+ log_file_error(filename, location.first_line,
+ "Cell instance refers to port %s which does not exist in module %s!.\n",
+ log_id(port_name), log_id(module->name));
+
+ // select the argument, if present
+ log_assert(child->children.size() <= 1);
+ if (child->children.empty())
+ continue;
+ AstNode *arg = child->children[0];
+
+ // plain identifiers never need indirection; this also prevents
+ // adding infinite levels of indirection
+ if (arg->type == AST_IDENTIFIER && arg->children.empty())
+ continue;
+
+ // only add indirection for standard inputs or outputs
+ if (ref->port_input == ref->port_output)
+ continue;
+
+ did_something = true;
+
+ // create the indirection wire
+ std::stringstream sstr;
+ sstr << "$indirect$" << ref->name.c_str() << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
+ std::string tmp_str = sstr.str();
+ add_wire_for_ref(ref, tmp_str);
+
+ AstNode *asgn = new AstNode(AST_ASSIGN);
+ current_ast_mod->children.push_back(asgn);
+
+ AstNode *ident = new AstNode(AST_IDENTIFIER);
+ ident->str = tmp_str;
+ child->children[0] = ident->clone();
+
+ if (ref->port_input && !ref->port_output) {
+ asgn->children.push_back(ident);
+ asgn->children.push_back(arg);
+ } else {
+ log_assert(!ref->port_input && ref->port_output);
+ asgn->children.push_back(arg);
+ asgn->children.push_back(ident);
+ }
+ }
+
+
+ }
+ }
+
int backup_width_hint = width_hint;
bool backup_sign_hint = sign_hint;
@@ -1168,8 +1506,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (const_fold && type == AST_CASE)
{
+ detectSignWidth(width_hint, sign_hint);
while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }
if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) {
+ RTLIL::Const case_expr = children[0]->bitsAsConst(width_hint, sign_hint);
std::vector<AstNode*> new_children;
new_children.push_back(children[0]);
for (int i = 1; i < GetSize(children); i++) {
@@ -1182,7 +1522,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
continue;
while (v->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }
if (v->type == AST_CONSTANT && v->bits_only_01()) {
- if (v->bits == children[0]->bits) {
+ RTLIL::Const case_item_expr = v->bitsAsConst(width_hint, sign_hint);
+ RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1);
+ log_assert(match.bits.size() == 1);
+ if (match.bits.front() == RTLIL::State::S1) {
while (i+1 < GetSize(children))
delete children[++i];
goto keep_const_cond;
@@ -1201,6 +1544,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
+ dict<std::string, pool<int>> backup_memwr_visible;
+ dict<std::string, pool<int>> final_memwr_visible;
+
+ if (type == AST_CASE && stage == 2) {
+ backup_memwr_visible = current_memwr_visible;
+ final_memwr_visible = current_memwr_visible;
+ }
+
// simplify all children first
// (iterate by index as e.g. auto wires can add new children in the process)
for (size_t i = 0; i < children.size(); i++) {
@@ -1263,11 +1614,25 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
flag_autowire = backup_flag_autowire;
unevaluated_tern_branch = backup_unevaluated_tern_branch;
+ if (stage == 2 && type == AST_CASE) {
+ for (auto &x : current_memwr_visible) {
+ for (int y : x.second)
+ final_memwr_visible[x.first].insert(y);
+ }
+ current_memwr_visible = backup_memwr_visible;
+ }
}
for (auto &attr : attributes) {
while (attr.second->simplify(true, false, false, stage, -1, false, true))
did_something = true;
}
+ if (type == AST_CASE && stage == 2) {
+ current_memwr_visible = final_memwr_visible;
+ }
+ if (type == AST_ALWAYS && stage == 2) {
+ current_memwr_visible.clear();
+ current_memwr_count.clear();
+ }
if (reset_width_after_children) {
width_hint = backup_width_hint;
@@ -1363,6 +1728,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) {
// replace with wire representing the packed structure
newNode = make_packed_struct(template_node, str);
+ newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
// add original input/output attribute to resolved wire
newNode->is_input = this->is_input;
newNode->is_output = this->is_output;
@@ -1411,18 +1777,33 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (is_custom_type) {
log_assert(children.size() == 2);
log_assert(children[1]->type == AST_WIRETYPE);
- if (!current_scope.count(children[1]->str))
- log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
- AstNode *resolved_type_node = current_scope.at(children[1]->str);
+ auto type_name = children[1]->str;
+ if (!current_scope.count(type_name)) {
+ log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str());
+ }
+ AstNode *resolved_type_node = current_scope.at(type_name);
if (resolved_type_node->type != AST_TYPEDEF)
- log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str());
+ log_file_error(filename, location.first_line, "`%s' does not name a type\n", type_name.c_str());
log_assert(resolved_type_node->children.size() == 1);
AstNode *template_node = resolved_type_node->children[0];
- delete children[1];
- children.pop_back();
// Ensure typedef itself is fully simplified
- while(template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+ while (template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+
+ if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) {
+ // replace with wire representing the packed structure
+ newNode = make_packed_struct(template_node, str);
+ newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
+ newNode->type = type;
+ current_scope[str] = this;
+ // copy param value, it needs to be 1st value
+ delete children[1];
+ children.pop_back();
+ newNode->children.insert(newNode->children.begin(), children[0]->clone());
+ goto apply_newNode;
+ }
+ delete children[1];
+ children.pop_back();
if (template_node->type == AST_MEMORY)
log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
@@ -1643,6 +2024,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
newNode = new AstNode(AST_IDENTIFIER, range);
newNode->str = sname;
newNode->basic_prep = true;
+ if (item_node->is_signed)
+ newNode = new AstNode(AST_TO_SIGNED, newNode);
goto apply_newNode;
}
}
@@ -1652,13 +2035,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_IDENTIFIER) {
if (current_scope.count(str) == 0) {
AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
- const std::string& mod_scope = current_scope_ast->str;
- if (str[0] == '\\' && str.substr(0, mod_scope.size()) == mod_scope) {
- std::string new_str = "\\" + str.substr(mod_scope.size() + 1);
- if (current_scope.count(new_str)) {
- str = new_str;
- }
- }
+ str = try_pop_module_prefix();
for (auto node : current_scope_ast->children) {
//log("looking at mod scope child %s\n", type2str(node->type).c_str());
switch (node->type) {
@@ -1712,7 +2089,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
// split memory access with bit select to individual statements
- if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue)
+ if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue && stage == 2)
{
if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)
log_file_error(filename, location.first_line, "Invalid bit-select on memory access!\n");
@@ -1964,6 +2341,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
{
expand_genblock(str + ".");
+ // if this is an autonamed block is in an always_comb
+ if (current_always && current_always->attributes.count(ID::always_comb)
+ && str.at(0) == '$')
+ // track local variables in this block so we can consider adding
+ // nosync once the block has been fully elaborated
+ for (AstNode *child : children)
+ if (child->type == AST_WIRE &&
+ !child->attributes.count(ID::nosync))
+ mark_auto_nosync(this, child);
+
std::vector<AstNode*> new_children;
for (size_t i = 0; i < children.size(); i++)
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) {
@@ -2180,6 +2567,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
children.push_back(node);
did_something = true;
}
+ else if (str == "buf" || str == "not")
+ {
+ AstNode *input = children_list.back();
+ if (str == "not")
+ input = new AstNode(AST_BIT_NOT, input);
+
+ newNode = new AstNode(AST_GENBLOCK);
+ for (auto it = children_list.begin(); it != std::prev(children_list.end()); it++) {
+ newNode->children.push_back(new AstNode(AST_ASSIGN, *it, input->clone()));
+ newNode->children.back()->was_checked = true;
+ }
+ delete input;
+
+ did_something = true;
+ }
else
{
AstNodeType op_type = AST_NONE;
@@ -2197,10 +2599,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
op_type = AST_BIT_XOR;
if (str == "xnor")
op_type = AST_BIT_XOR, invert_results = true;
- if (str == "buf")
- op_type = AST_POS;
- if (str == "not")
- op_type = AST_POS, invert_results = true;
log_assert(op_type != AST_NONE);
AstNode *node = children_list[1];
@@ -2252,6 +2650,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
+ delete left_at_zero_ast;
+ delete right_at_zero_ast;
}
bool use_case_method = false;
@@ -2554,12 +2954,12 @@ skip_dynamic_range_lvalue_expansion:;
current_scope[wire_addr->str] = wire_addr;
while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
- AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
+ AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
assign_addr->children[0]->str = id_addr;
assign_addr->children[0]->was_checked = true;
defNode->children.push_back(assign_addr);
- assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
+ assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
assign_addr->children[0]->str = id_addr;
assign_addr->children[0]->was_checked = true;
newNode->children.push_back(assign_addr);
@@ -2580,7 +2980,7 @@ skip_dynamic_range_lvalue_expansion:;
current_scope[wire_data->str] = wire_data;
while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
- AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
+ AstNode *assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
assign_data->children[0]->str = id_data;
assign_data->children[0]->was_checked = true;
defNode->children.push_back(assign_data);
@@ -2589,25 +2989,20 @@ skip_dynamic_range_lvalue_expansion:;
node_data->str = id_data;
}
- AstNode *node_en = nullptr;
- if (current_always->type == AST_INITIAL) {
- node_en = AstNode::mkconst_int(1, false);
- } else {
- AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
- wire_en->str = id_en;
- wire_en->was_checked = true;
- current_ast_mod->children.push_back(wire_en);
- current_scope[wire_en->str] = wire_en;
- while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
-
- AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
- assign_en->children[0]->str = id_en;
- assign_en->children[0]->was_checked = true;
- defNode->children.push_back(assign_en);
+ AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ wire_en->str = id_en;
+ wire_en->was_checked = true;
+ current_ast_mod->children.push_back(wire_en);
+ current_scope[wire_en->str] = wire_en;
+ while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
- node_en = new AstNode(AST_IDENTIFIER);
- node_en->str = id_en;
- }
+ AstNode *assign_en_first = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
+ assign_en_first->children[0]->str = id_en;
+ assign_en_first->children[0]->was_checked = true;
+ defNode->children.push_back(assign_en_first);
+
+ AstNode *node_en = new AstNode(AST_IDENTIFIER);
+ node_en->str = id_en;
if (!defNode->children.empty())
current_top_block->children.insert(current_top_block->children.begin(), defNode);
@@ -2626,18 +3021,16 @@ skip_dynamic_range_lvalue_expansion:;
std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx);
- assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone()));
assign_data->children[0]->str = id_data;
assign_data->children[0]->was_checked = true;
- if (current_always->type != AST_INITIAL) {
- for (int i = 0; i < mem_width; i++)
- set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
- assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
- assign_en->children[0]->str = id_en;
- assign_en->children[0]->was_checked = true;
- }
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
+ assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+ assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
}
else
{
@@ -2655,19 +3048,17 @@ skip_dynamic_range_lvalue_expansion:;
log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
- assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
assign_data->children[0]->str = id_data;
assign_data->children[0]->was_checked = true;
- if (current_always->type != AST_INITIAL) {
- for (int i = 0; i < mem_width; i++)
- set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
- assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
- new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
- assign_en->children[0]->str = id_en;
- assign_en->children[0]->was_checked = true;
- }
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
+ assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER),
+ new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
+ assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
delete left_at_zero_ast;
delete right_at_zero_ast;
@@ -2677,26 +3068,42 @@ skip_dynamic_range_lvalue_expansion:;
else
{
if (!(children[0]->children.size() == 1 && children[1]->isConst())) {
- assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
+ assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[1]->clone());
assign_data->children[0]->str = id_data;
assign_data->children[0]->was_checked = true;
}
- if (current_always->type != AST_INITIAL) {
- assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
- assign_en->children[0]->str = id_en;
- assign_en->children[0]->was_checked = true;
- }
+ assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+ assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
}
if (assign_data)
newNode->children.push_back(assign_data);
if (assign_en)
newNode->children.push_back(assign_en);
- AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR, node_addr, node_data, node_en);
+ AstNode *wrnode;
+ if (current_always->type == AST_INITIAL)
+ wrnode = new AstNode(AST_MEMINIT, node_addr, node_data, node_en, mkconst_int(1, false));
+ else
+ wrnode = new AstNode(AST_MEMWR, node_addr, node_data, node_en);
wrnode->str = children[0]->str;
wrnode->id2ast = children[0]->id2ast;
- current_ast_mod->children.push_back(wrnode);
+ wrnode->location = location;
+ if (wrnode->type == AST_MEMWR) {
+ int portid = current_memwr_count[wrnode->str]++;
+ wrnode->children.push_back(mkconst_int(portid, false));
+ std::vector<RTLIL::State> priority_mask;
+ for (int i = 0; i < portid; i++) {
+ bool has_prio = current_memwr_visible[wrnode->str].count(i);
+ priority_mask.push_back(State(has_prio));
+ }
+ wrnode->children.push_back(mkconst_bits(priority_mask, false));
+ current_memwr_visible[wrnode->str].insert(portid);
+ current_always->children.push_back(wrnode);
+ } else {
+ current_ast_mod->children.push_back(wrnode);
+ }
if (newNode->children.empty()) {
delete newNode;
@@ -3469,6 +3876,8 @@ skip_dynamic_range_lvalue_expansion:;
// convert purely constant arguments into localparams
if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child)) {
wire->type = AST_LOCALPARAM;
+ if (wire->attributes.count(ID::nosync))
+ delete wire->attributes.at(ID::nosync);
wire->attributes.erase(ID::nosync);
wire->children.insert(wire->children.begin(), arg->clone());
// args without a range implicitly have width 1
@@ -3492,6 +3901,7 @@ skip_dynamic_range_lvalue_expansion:;
}
// updates the sizing
while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+ delete arg;
continue;
}
AstNode *wire_id = new AstNode(AST_IDENTIFIER);
@@ -3773,7 +4183,11 @@ replace_fcall_later:;
case AST_CAST_SIZE:
if (children.at(0)->type == AST_CONSTANT && children.at(1)->type == AST_CONSTANT) {
int width = children[0]->bitsAsConst().as_int();
- RTLIL::Const val = children[1]->bitsAsConst(width);
+ RTLIL::Const val;
+ if (children[1]->is_unsized)
+ val = children[1]->bitsAsUnsizedConst(width);
+ else
+ val = children[1]->bitsAsConst(width);
newNode = mkconst_bits(val.bits, children[1]->is_signed);
}
break;
@@ -3841,8 +4255,12 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
AstNode *meminit = nullptr;
int next_meminit_cursor=0;
vector<State> meminit_bits;
+ vector<State> en_bits;
int meminit_size=0;
+ for (int i = 0; i < mem_width; i++)
+ en_bits.push_back(State::S1);
+
std::ifstream f;
f.open(mem_filename.c_str());
if (f.fail()) {
@@ -3916,12 +4334,13 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
{
if (meminit != nullptr) {
meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
- meminit->children[2] = AstNode::mkconst_int(meminit_size, false);
+ meminit->children[3] = AstNode::mkconst_int(meminit_size, false);
}
meminit = new AstNode(AST_MEMINIT);
meminit->children.push_back(AstNode::mkconst_int(cursor, false));
meminit->children.push_back(nullptr);
+ meminit->children.push_back(AstNode::mkconst_bits(en_bits, false));
meminit->children.push_back(nullptr);
meminit->str = memory->str;
meminit->id2ast = memory;
@@ -3956,7 +4375,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
if (meminit != nullptr) {
meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false);
- meminit->children[2] = AstNode::mkconst_int(meminit_size, false);
+ meminit->children[3] = AstNode::mkconst_int(meminit_size, false);
}
return block;
@@ -3967,7 +4386,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
// prefix is carried forward, but resolution of their children is deferred
void AstNode::expand_genblock(const std::string &prefix)
{
- if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) {
+ if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE || type == AST_PREFIX) {
log_assert(!str.empty());
// search starting in the innermost scope and then stepping outward
@@ -4053,10 +4472,15 @@ void AstNode::expand_genblock(const std::string &prefix)
for (size_t i = 0; i < children.size(); i++) {
AstNode *child = children[i];
- // AST_PREFIX member names should not be prefixed; a nested AST_PREFIX
- // still needs to recursed-into
- if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER)
+ // AST_PREFIX member names should not be prefixed; we recurse into them
+ // as normal to ensure indices and ranges are properly resolved, and
+ // then restore the previous string
+ if (type == AST_PREFIX && i == 1) {
+ std::string backup_scope_name = child->str;
+ child->expand_genblock(prefix);
+ child->str = backup_scope_name;
continue;
+ }
// functions/tasks may reference wires, constants, etc. in this scope
if (child->type == AST_FUNCTION || child->type == AST_TASK)
continue;
@@ -4301,10 +4725,12 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
log_assert(children[0]->type == AST_CONSTANT);
log_assert(children[1]->type == AST_CONSTANT);
log_assert(children[2]->type == AST_CONSTANT);
+ log_assert(children[3]->type == AST_CONSTANT);
int cursor = children[0]->asInt(false);
Const data = children[1]->bitsAsConst();
- int length = children[2]->asInt(false);
+ Const en = children[2]->bitsAsConst();
+ int length = children[3]->asInt(false);
if (length != 0)
{
@@ -4315,10 +4741,37 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
int wordsz = GetSize(data) / length;
for (int i = 0; i < length; i++) {
- block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false))), mkconst_bits(data.extract(i*wordsz, wordsz).bits, false)));
- block->children.back()->children[0]->str = str;
- block->children.back()->children[0]->id2ast = id2ast;
- block->children.back()->children[0]->was_checked = true;
+ int pos = 0;
+ while (pos < wordsz) {
+ if (en[pos] != State::S1) {
+ pos++;
+ } else {
+ int epos = pos + 1;
+ while (epos < wordsz && en[epos] == State::S1)
+ epos++;
+ int clen = epos - pos;
+ AstNode *range = new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false));
+ if (pos != 0 || epos != wordsz) {
+ int left;
+ int right;
+ AstNode *mrange = id2ast->children[0];
+ if (mrange->range_left < mrange->range_right) {
+ right = mrange->range_right - pos;
+ left = mrange->range_right - epos + 1;
+ } else {
+ right = mrange->range_right + pos;
+ left = mrange->range_right + epos - 1;
+ }
+ range = new AstNode(AST_MULTIRANGE, range, new AstNode(AST_RANGE, AstNode::mkconst_int(left, true), AstNode::mkconst_int(right, true)));
+ }
+ AstNode *target = new AstNode(AST_IDENTIFIER, range);
+ target->str = str;
+ target->id2ast = id2ast;
+ target->was_checked = true;
+ block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).bits, false)));
+ pos = epos;
+ }
+ }
}
}
@@ -4422,11 +4875,48 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
if (children[0]->children[0]->type == AST_CONSTANT)
{
int id = children[0]->children[0]->integer;
- str = stringf("%s[%d]", str.c_str(), id);
+ int left = id2ast->children[1]->children[0]->integer;
+ int right = id2ast->children[1]->children[1]->integer;
+ bool valid_const_access =
+ (left <= id && id <= right) ||
+ (right <= id && id <= left);
+ if (valid_const_access)
+ {
+ str = stringf("%s[%d]", str.c_str(), id);
+ delete_children();
+ range_valid = false;
+ id2ast = NULL;
+ }
+ else
+ {
+ int width;
+ if (bit_part_sel)
+ {
+ bit_part_sel->dumpAst(nullptr, "? ");
+ if (bit_part_sel->children.size() == 1)
+ width = 0;
+ else
+ width = bit_part_sel->children[0]->integer -
+ bit_part_sel->children[1]->integer;
+ delete bit_part_sel;
+ bit_part_sel = nullptr;
+ }
+ else
+ {
+ width = id2ast->children[0]->children[0]->integer -
+ id2ast->children[0]->children[1]->integer;
+ }
+ width = abs(width) + 1;
- delete_children();
- range_valid = false;
- id2ast = NULL;
+ delete_children();
+
+ std::vector<RTLIL::State> x_bits;
+ for (int i = 0; i < width; i++)
+ x_bits.push_back(RTLIL::State::Sx);
+ AstNode *constant = AstNode::mkconst_bits(x_bits, false);
+ constant->cloneInto(this);
+ delete constant;
+ }
}
else
{
@@ -4663,6 +5153,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
{
std::map<std::string, AstNode*> backup_scope = current_scope;
std::map<std::string, AstNode::varinfo_t> variables;
+ std::vector<AstNode*> to_delete;
AstNode *block = new AstNode(AST_BLOCK);
AstNode *result = nullptr;
@@ -4720,6 +5211,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
current_scope[stmt->str] = stmt;
block->children.erase(block->children.begin());
+ to_delete.push_back(stmt);
continue;
}
@@ -4732,6 +5224,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
current_scope[stmt->str] = stmt;
block->children.erase(block->children.begin());
+ to_delete.push_back(stmt);
continue;
}
@@ -4927,12 +5420,20 @@ finished:
delete block;
current_scope = backup_scope;
+ for (auto it : to_delete) {
+ delete it;
+ }
+ to_delete.clear();
+
return result;
}
void AstNode::allocateDefaultEnumValues()
{
log_assert(type==AST_ENUM);
+ log_assert(children.size() > 0);
+ if (children.front()->attributes.count(ID::enum_base_type))
+ return; // already elaborated
int last_enum_int = -1;
for (auto node : children) {
log_assert(node->type==AST_ENUM_ITEM);
@@ -5008,4 +5509,21 @@ std::pair<AstNode*, AstNode*> AstNode::get_tern_choice()
return {choice, not_choice};
}
+std::string AstNode::try_pop_module_prefix() const
+{
+ AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
+ size_t pos = str.find('.', 1);
+ if (str[0] == '\\' && pos != std::string::npos) {
+ std::string new_str = "\\" + str.substr(pos + 1);
+ if (current_scope.count(new_str)) {
+ std::string prefix = str.substr(0, pos);
+ auto it = current_scope_ast->attributes.find(ID::hdlname);
+ if ((it != current_scope_ast->attributes.end() && it->second->str == prefix)
+ || prefix == current_scope_ast->str)
+ return new_str;
+ }
+ }
+ return str;
+}
+
YOSYS_NAMESPACE_END