aboutsummaryrefslogtreecommitdiffstats
path: root/frontends/ast
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/ast')
-rw-r--r--frontends/ast/ast.cc28
-rw-r--r--frontends/ast/ast.h28
-rw-r--r--frontends/ast/dpicall.cc17
-rw-r--r--frontends/ast/genrtlil.cc140
-rw-r--r--frontends/ast/simplify.cc685
5 files changed, 593 insertions, 305 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 1c0a8b34d..57552d86c 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -287,8 +287,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)
@@ -548,9 +547,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());
@@ -959,6 +958,16 @@ RTLIL::Const AstNode::realAsConst(int width)
return result;
}
+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();
+}
+
// 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)
{
@@ -974,8 +983,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
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);
+ set_src_attr(current_module, ast);
current_module->set_bool_attribute(ID::cells_not_processed);
current_ast_mod = ast;
@@ -1229,13 +1237,13 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
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());
} 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",
+ (*it)->str.c_str(), (*it)->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);
+ (*it)->str.c_str(), (*it)->loc_string().c_str());
design->remove(existing_mod);
}
}
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 907392166..1c9a6ee47 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -252,8 +252,8 @@ namespace AST
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, bool original_scope = true);
- 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);
@@ -263,14 +263,22 @@ namespace AST
bool detect_latch(const std::string &var);
// 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);
- bool has_const_only_constructs(std::set<std::string>& visited, 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;
@@ -315,6 +323,9 @@ namespace AST
// helpers for enum
void allocateDefaultEnumValues();
void annotateTypedEnums(AstNode *template_node);
+
+ // helpers for locations
+ std::string loc_string() const;
};
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
@@ -353,6 +364,9 @@ 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);
}
namespace AST_INTERNAL
diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc
index e241142d3..948c9083c 100644
--- a/frontends/ast/dpicall.cc
+++ b/frontends/ast/dpicall.cc
@@ -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 b8bfdf65e..d4299bf69 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -45,10 +45,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 +77,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 +104,10 @@ 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) {
@@ -137,10 +139,10 @@ 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) {
@@ -318,7 +320,7 @@ struct AST_INTERNAL::ProcessGenerator
// 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);
+ 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)
@@ -354,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");
}
@@ -454,7 +456,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;
@@ -589,7 +591,7 @@ struct AST_INTERNAL::ProcessGenerator
case AST_CASE:
{
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);
+ set_src_attr(sw, ast);
sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap());
current_case->switches.push_back(sw);
@@ -623,7 +625,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) {
@@ -942,6 +944,41 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
}
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.
@@ -965,6 +1002,29 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real
detectSignWidthWorker(width_hint, sign_hint, found_real);
}
+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
// all generated cells, wires and processes are added to the module pointed to by 'current_module'
// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned.
@@ -1010,8 +1070,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;
@@ -1048,10 +1110,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) {
@@ -1064,16 +1129,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;
@@ -1094,9 +1159,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);
@@ -1105,7 +1167,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) {
@@ -1115,6 +1177,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) {
@@ -1162,7 +1225,7 @@ 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)
log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str());
@@ -1544,13 +1607,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();
@@ -1582,7 +1646,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
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);
+ set_src_attr(cell, this);
int mem_width, mem_size, addr_bits;
id2ast->meminfo(mem_width, mem_size, addr_bits);
@@ -1645,8 +1709,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)
@@ -1687,11 +1752,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);
@@ -1740,7 +1804,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// 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, arg->bits.size());
+ RTLIL::Wire *wire = current_module->addWire(wire_name, GetSize(sig));
wire->is_signed = true;
current_module->connect(wire, sig);
sig = wire;
@@ -1855,7 +1919,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)) {
@@ -1866,7 +1930,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;
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index d4242f1e7..5c4dd290f 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -549,6 +549,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var)
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;
+}
+
// 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().
@@ -561,10 +571,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;
@@ -748,6 +760,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];
@@ -928,7 +943,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) {
@@ -942,7 +957,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;
}
@@ -1078,7 +1093,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;
@@ -1111,6 +1125,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;
@@ -1174,6 +1206,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
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)
@@ -1186,6 +1219,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;
@@ -1205,11 +1242,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
current_block = this;
current_block_child = children[i];
}
- if (!in_param_here && type == AST_FCALL) {
- bool recommend_const_eval = false;
- bool require_const_eval = has_const_only_constructs(recommend_const_eval);
- in_param_here = recommend_const_eval || require_const_eval;
- }
if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK)
current_top_block = children[i];
if (i == 0 && child_0_is_self_determined)
@@ -1230,6 +1262,7 @@ 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;
}
for (auto &attr : attributes) {
while (attr.second->simplify(true, false, false, stage, -1, false, true))
@@ -1330,6 +1363,9 @@ 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);
+ // 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;
}
@@ -1852,19 +1888,24 @@ 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++) {
@@ -1912,14 +1953,16 @@ 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 + ".");
std::vector<AstNode*> new_children;
for (size_t i = 0; i < children.size(); i++)
@@ -1939,8 +1982,7 @@ 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++) {
@@ -1976,8 +2018,7 @@ 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++) {
@@ -2055,8 +2096,7 @@ 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++) {
@@ -3049,6 +3089,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];
@@ -3156,16 +3283,21 @@ 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)) { }
@@ -3174,12 +3306,14 @@ skip_dynamic_range_lvalue_expansion:;
}
if (all_args_const) {
- AstNode *func_workspace = current_scope[str]->clone();
- func_workspace->str = NEW_ID.str();
- func_workspace->replace_result_wire_name_in_function(str, func_workspace->str);
- 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)
@@ -3189,8 +3323,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;
@@ -3200,16 +3332,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)) { }
@@ -3253,7 +3386,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;
@@ -3315,7 +3447,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;
@@ -3326,15 +3457,11 @@ 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())
{
@@ -3363,6 +3490,8 @@ skip_dynamic_range_lvalue_expansion:;
range->children.push_back(mkconst_int(0, true));
}
}
+ // updates the sizing
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
continue;
}
AstNode *wire_id = new AstNode(AST_IDENTIFIER);
@@ -3378,18 +3507,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());
@@ -3402,10 +3522,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 = "";
@@ -3605,24 +3726,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) {
@@ -3856,63 +3962,52 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
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, bool original_scope)
+// 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)
{
- // `original_scope` defaults to false, and is used to prevent the premature
- // prefixing of items in named sub-blocks
-
- 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) {
- if (name_map.count(str) > 0) {
- str = name_map[str];
- } else {
- // remap the prefix of this ident if it is a local generate scope
- size_t pos = str.rfind('.');
- if (pos != std::string::npos) {
- std::string existing_prefix = str.substr(0, pos);
- if (name_map.count(existing_prefix) > 0) {
- str = name_map[existing_prefix] + str.substr(pos);
- }
+ 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;
}
- }
- }
- std::map<std::string, std::string> backup_name_map;
-
- auto prefix_node = [&](AstNode* child) {
- if (backup_name_map.size() == 0)
- backup_name_map = name_map;
+ // 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 within a nested scope
- if (!original_scope) {
- // this declaration shadows anything in the parent scope(s)
- name_map[child->str] = child->str;
- return;
}
+ }
- 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;
+ 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
@@ -3964,43 +4059,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
continue;
// functions/tasks may reference wires, constants, etc. in this scope
if (child->type == AST_FUNCTION || child->type == AST_TASK)
- child->expand_genblock(index_var, prefix, name_map, false);
- // continue prefixing if this child block is anonymous
- else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK)
- child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty());
- else
- child->expand_genblock(index_var, prefix, name_map, original_scope);
- }
-
+ 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 (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;
}
}
@@ -4492,33 +4599,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()
{
- std::set<std::string> visited;
- return has_const_only_constructs(visited, recommend_const_eval);
-}
-
-bool AstNode::has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval)
-{
- if (type == AST_FUNCTION || type == AST_TASK)
- {
- if (visited.count(str))
- {
- recommend_const_eval = true;
- return false;
- }
- visited.insert(str);
- }
-
- 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(visited, recommend_const_eval))
- return true;
for (auto child : children)
- if (child->AstNode::has_const_only_constructs(visited, recommend_const_eval))
+ if (child->has_const_only_constructs())
return true;
return false;
}
@@ -4534,19 +4620,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);
}
@@ -4556,19 +4649,22 @@ 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;
AstNode *block = new AstNode(AST_BLOCK);
+ AstNode *result = nullptr;
size_t argidx = 0;
for (auto child : children)
@@ -4590,24 +4686,37 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
if (stmt->type == AST_WIRE)
{
while (stmt->simplify(true, false, false, 1, -1, false, true)) { }
- if (!stmt->range_valid)
- log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n",
- stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column);
- variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1);
- variables[stmt->str].offset = min(stmt->range_left, stmt->range_right);
- variables[stmt->str].is_signed = stmt->is_signed;
+ 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()) {
- int width = variables[stmt->str].val.bits.size();
- auto* arg_node = fcall->children.at(argidx++);
- if (arg_node->type == AST_CONSTANT) {
- variables[stmt->str].val = arg_node->bitsAsConst(width);
+ 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(arg_node->type == AST_REALVALUE);
- variables[stmt->str].val = arg_node->realAsConst(width);
+ log_assert(variable.arg->type == AST_REALVALUE);
+ variable.val = variable.arg->realAsConst(width);
}
}
- if (!backup_scope.count(stmt->str))
- backup_scope[stmt->str] = current_scope[stmt->str];
current_scope[stmt->str] = stmt;
block->children.erase(block->children.begin());
@@ -4620,8 +4729,6 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
{
while (stmt->simplify(true, false, false, 1, -1, false, true)) { }
- if (!backup_scope.count(stmt->str))
- backup_scope[stmt->str] = current_scope[stmt->str];
current_scope[stmt->str] = stmt;
block->children.erase(block->children.begin());
@@ -4632,32 +4739,46 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
{
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];
@@ -4684,12 +4805,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());
@@ -4705,12 +4830,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++)
@@ -4724,7 +4853,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;
@@ -4741,14 +4871,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;
@@ -4770,6 +4904,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();
@@ -4777,20 +4914,20 @@ 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();
}
- delete block;
+ result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed);
- for (auto &it : backup_scope)
- if (it.second == NULL)
- current_scope.erase(it.first);
- else
- current_scope[it.first] = it.second;
+finished:
+ delete block;
+ current_scope = backup_scope;
- return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed);
+ return result;
}
void AstNode::allocateDefaultEnumValues()
@@ -4821,4 +4958,54 @@ 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};
+}
+
YOSYS_NAMESPACE_END