summaryrefslogtreecommitdiff
path: root/frontends/ast/simplify.cc
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/ast/simplify.cc')
-rw-r--r--frontends/ast/simplify.cc412
1 files changed, 361 insertions, 51 deletions
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 2621be49..57aa648c 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -63,7 +63,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
#if 0
log("-------------\n");
- log("AST simplify[%d] depth %d at %s:%d,\n", stage, recursion_counter, filename.c_str(), linenum);
+ log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, filename.c_str(), linenum, type2str(type).c_str(), this);
log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n",
int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param));
// dumpAst(NULL, "> ");
@@ -148,14 +148,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
- while (mem2reg_as_needed_pass2(mem2reg_set, this, NULL)) { }
+ AstNode *async_block = NULL;
+ while (mem2reg_as_needed_pass2(mem2reg_set, this, NULL, async_block)) { }
- for (size_t i = 0; i < children.size(); i++) {
- if (mem2reg_set.count(children[i]) > 0) {
- delete children[i];
- children.erase(children.begin() + (i--));
- }
- }
+ vector<AstNode*> delnodes;
+ mem2reg_remove(mem2reg_set, delnodes);
+
+ for (auto node : delnodes)
+ delete node;
}
while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { }
@@ -174,8 +174,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
// deactivate all calls to non-synthesis system tasks
- // note that $display and $finish are used for synthesis-time DRC so they're not in this list
- if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" || str == "$stop" ||
+ // note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list
+ if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" ||
str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) {
log_warning("Ignoring call to system %s %s at %s:%d.\n", type == AST_FCALL ? "function" : "task", str.c_str(), filename.c_str(), linenum);
delete_children();
@@ -193,13 +193,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// but should be good enough for most uses
if ((type == AST_TCALL) && ((str == "$display") || (str == "$write")))
{
- size_t nargs = GetSize(children);
- if(nargs < 1)
+ int nargs = GetSize(children);
+ if (nargs < 1)
log_error("System task `%s' got %d arguments, expected >= 1 at %s:%d.\n",
str.c_str(), int(children.size()), filename.c_str(), linenum);
// First argument is the format string
- AstNode *node_string = children[0]->clone();
+ AstNode *node_string = children[0];
while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (node_string->type != AST_CONSTANT)
log_error("Failed to evaluate system task `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
@@ -207,37 +207,57 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// Other arguments are placeholders. Process the string as we go through it
std::string sout;
- size_t next_arg = 1;
- for(size_t i=0; i<sformat.length(); i++)
+ int next_arg = 1;
+ for (size_t i = 0; i < sformat.length(); i++)
{
// format specifier
- if(sformat[i] == '%')
+ if (sformat[i] == '%')
{
// If there's no next character, that's a problem
- if(i+1 >= sformat.length())
+ if (i+1 >= sformat.length())
log_error("System task `%s' called with `%%' at end of string at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
char cformat = sformat[++i];
// %% is special, does not need a matching argument
- if(cformat == '%')
+ if (cformat == '%')
{
sout += '%';
continue;
}
- // If we're out of arguments, that's a problem!
- if(next_arg >= nargs)
- log_error("System task `%s' called with more format specifiers than arguments at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
-
// Simplify the argument
- AstNode *node_arg = children[next_arg ++]->clone();
- while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
- if (node_arg->type != AST_CONSTANT)
- log_error("Failed to evaluate system task `%s' with non-constant argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ AstNode *node_arg = nullptr;
// Everything from here on depends on the format specifier
- switch(cformat)
+ switch (cformat)
+ {
+ case 's':
+ case 'S':
+ case 'd':
+ case 'D':
+ case 'x':
+ case 'X':
+ if (next_arg >= GetSize(children))
+ log_error("Missing argument for %%%c format specifier in system task `%s' at %s:%d.\n",
+ cformat, str.c_str(), filename.c_str(), linenum);
+
+ node_arg = children[next_arg++];
+ while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_arg->type != AST_CONSTANT)
+ log_error("Failed to evaluate system task `%s' with non-constant argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ break;
+
+ case 'm':
+ case 'M':
+ break;
+
+ default:
+ log_error("System task `%s' called with invalid/unsupported format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ break;
+ }
+
+ switch (cformat)
{
case 's':
case 'S':
@@ -262,9 +282,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
break;
- default:
- log_error("System task `%s' called with invalid format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ case 'm':
+ case 'M':
+ sout += log_id(current_module->name);
break;
+
+ default:
+ log_abort();
}
}
@@ -275,7 +299,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// Finally, print the message (only include a \n for $display, not for $write)
log("%s", sout.c_str());
- if(str == "$display")
+ if (str == "$display")
log("\n");
delete_children();
str = std::string();
@@ -373,9 +397,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
auto backup_current_block_child = current_block_child;
auto backup_current_top_block = current_top_block;
auto backup_current_always = current_always;
+ auto backup_current_always_clocked = current_always_clocked;
if (type == AST_ALWAYS || type == AST_INITIAL)
+ {
current_always = this;
+ current_always_clocked = false;
+
+ if (type == AST_ALWAYS)
+ for (auto child : children)
+ if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
+ current_always_clocked = true;
+ }
int backup_width_hint = width_hint;
bool backup_sign_hint = sign_hint;
@@ -489,6 +522,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
children_are_self_determined = true;
break;
+ case AST_FCALL:
+ case AST_TCALL:
+ children_are_self_determined = true;
+ break;
+
default:
width_hint = -1;
sign_hint = false;
@@ -504,6 +542,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
detectSignWidth(width_hint, sign_hint);
}
+ if (type == AST_FCALL && str == "\\$past")
+ detectSignWidth(width_hint, sign_hint);
+
if (type == AST_TERNARY) {
int width_hint_left, width_hint_right;
bool sign_hint_left, sign_hint_right;
@@ -516,6 +557,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
+ if (type == AST_CONDX && children.size() > 0 && children.at(0)->type == AST_CONSTANT) {
+ for (auto &bit : children.at(0)->bits)
+ if (bit == State::Sz || bit == State::Sx)
+ bit = State::Sa;
+ }
+
+ if (type == AST_CONDZ && children.size() > 0 && children.at(0)->type == AST_CONSTANT) {
+ for (auto &bit : children.at(0)->bits)
+ if (bit == State::Sz)
+ bit = State::Sa;
+ }
+
if (const_fold && type == AST_CASE)
{
while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }
@@ -524,7 +577,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
new_children.push_back(children[0]);
for (int i = 1; i < GetSize(children); i++) {
AstNode *child = children[i];
- log_assert(child->type == AST_COND);
+ log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ);
for (auto v : child->children) {
if (v->type == AST_DEFAULT)
goto keep_const_cond;
@@ -616,6 +669,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
current_block_child = backup_current_block_child;
current_top_block = backup_current_top_block;
current_always = backup_current_always;
+ current_always_clocked = backup_current_always_clocked;
for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) {
if (it->second == NULL)
@@ -794,7 +848,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
RTLIL::SigSpec sig(children[0]->bits);
sig.extend_u0(width, children[0]->is_signed);
AstNode *old_child_0 = children[0];
- children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed);
+ children[0] = mkconst_bits(sig.as_const().bits, is_signed);
delete old_child_0;
}
children[0]->is_signed = is_signed;
@@ -847,11 +901,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
int mem_width, mem_size, addr_bits;
id2ast->meminfo(mem_width, mem_size, addr_bits);
+ int data_range_left = id2ast->children[0]->range_left;
+ int data_range_right = id2ast->children[0]->range_right;
+
std::stringstream sstr;
- sstr << "$mem2bits$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
+ sstr << "$mem2bits$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++);
std::string wire_id = sstr.str();
- AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true)));
wire->str = wire_id;
if (current_block)
wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
@@ -1101,7 +1158,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
AstNode *selected_case = NULL;
for (size_t i = 1; i < children.size(); i++)
{
- log_assert(children.at(i)->type == AST_COND);
+ log_assert(children.at(i)->type == AST_COND || children.at(i)->type == AST_CONDX || children.at(i)->type == AST_CONDZ);
AstNode *this_genblock = NULL;
for (auto child : children.at(i)->children) {
@@ -1316,7 +1373,7 @@ skip_dynamic_range_lvalue_expansion:;
if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && current_block != NULL)
{
std::stringstream sstr;
- sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++);
+ sstr << "$formal$" << filename << ":" << linenum << "$" << (autoidx++);
std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN";
AstNode *wire_check = new AstNode(AST_WIRE);
@@ -1328,8 +1385,10 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *wire_en = new AstNode(AST_WIRE);
wire_en->str = id_en;
current_ast_mod->children.push_back(wire_en);
- current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)))));
- current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en;
+ if (current_always_clocked) {
+ current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)))));
+ current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en;
+ }
current_scope[wire_en->str] = wire_en;
while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
@@ -1350,7 +1409,12 @@ skip_dynamic_range_lvalue_expansion:;
assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone()));
assign_check->children[0]->str = id_check;
- assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
+ if (current_always == nullptr || current_always->type != AST_INITIAL) {
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
+ } else {
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_FCALL));
+ assign_en->children[1]->str = "\\$initstate";
+ }
assign_en->children[0]->str = id_en;
newNode = new AstNode(AST_BLOCK);
@@ -1383,6 +1447,50 @@ skip_dynamic_range_lvalue_expansion:;
goto apply_newNode;
}
+ // assignment with nontrivial member in left-hand concat expression -> split assignment
+ if ((type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_CONCAT && width_hint > 0)
+ {
+ bool found_nontrivial_member = false;
+
+ for (auto child : children[0]->children) {
+ if (child->type == AST_IDENTIFIER && child->id2ast != NULL && child->id2ast->type == AST_MEMORY)
+ found_nontrivial_member = true;
+ }
+
+ if (found_nontrivial_member)
+ {
+ newNode = new AstNode(AST_BLOCK);
+
+ AstNode *wire_tmp = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true)));
+ wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", filename.c_str(), linenum, autoidx++);
+ current_ast_mod->children.push_back(wire_tmp);
+ current_scope[wire_tmp->str] = wire_tmp;
+ wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER);
+ wire_tmp_id->str = wire_tmp->str;
+
+ newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, wire_tmp_id, children[1]->clone()));
+
+ int cursor = 0;
+ for (auto child : children[0]->children)
+ {
+ int child_width_hint = -1;
+ bool child_sign_hint = true;
+ child->detectSignWidth(child_width_hint, child_sign_hint);
+
+ AstNode *rhs = wire_tmp_id->clone();
+ rhs->children.push_back(new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+child_width_hint-1, true), AstNode::mkconst_int(cursor, true)));
+ newNode->children.push_back(new AstNode(type, child->clone(), rhs));
+
+ cursor += child_width_hint;
+ }
+
+ goto apply_newNode;
+ }
+ }
+
// assignment with memory in left-hand side expression -> replace with memory write port
if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 &&
@@ -1404,6 +1512,15 @@ skip_dynamic_range_lvalue_expansion:;
int mem_width, mem_size, addr_bits;
children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits);
+ int data_range_left = children[0]->id2ast->children[0]->range_left;
+ int data_range_right = children[0]->id2ast->children[0]->range_right;
+ int mem_data_range_offset = std::min(data_range_left, data_range_right);
+
+ int addr_width_hint = -1;
+ bool addr_sign_hint = true;
+ children[0]->children[0]->children[0]->detectSignWidthWorker(addr_width_hint, addr_sign_hint);
+ addr_bits = std::max(addr_bits, addr_width_hint);
+
AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
wire_addr->str = id_addr;
current_ast_mod->children.push_back(wire_addr);
@@ -1461,6 +1578,7 @@ skip_dynamic_range_lvalue_expansion:;
{
int offset = children[0]->children[1]->range_right;
int width = children[0]->children[1]->range_left - offset + 1;
+ offset -= mem_data_range_offset;
std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx);
@@ -1482,6 +1600,9 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *right_at_zero_ast = the_range->children.size() >= 2 ? the_range->children[1]->clone() : left_at_zero_ast->clone();
AstNode *offset_ast = right_at_zero_ast->clone();
+ if (mem_data_range_offset)
+ offset_ast = new AstNode(AST_SUB, offset_ast, mkconst_int(mem_data_range_offset, true));
+
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
@@ -1545,6 +1666,153 @@ skip_dynamic_range_lvalue_expansion:;
{
if (type == AST_FCALL)
{
+ if (str == "\\$initstate")
+ {
+ int myidx = autoidx++;
+
+ AstNode *wire = new AstNode(AST_WIRE);
+ wire->str = stringf("$initstate$%d_wire", myidx);
+ current_ast_mod->children.push_back(wire);
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE), new AstNode(AST_ARGUMENT, new AstNode(AST_IDENTIFIER)));
+ cell->str = stringf("$initstate$%d", myidx);
+ cell->children[0]->str = "$initstate";
+ cell->children[1]->str = "\\Y";
+ cell->children[1]->children[0]->str = wire->str;
+ cell->children[1]->children[0]->id2ast = wire;
+ current_ast_mod->children.push_back(cell);
+ while (cell->simplify(true, false, false, 1, -1, false, false)) { }
+
+ newNode = new AstNode(AST_IDENTIFIER);
+ newNode->str = wire->str;
+ newNode->id2ast = wire;
+ goto apply_newNode;
+ }
+
+ if (str == "\\$past")
+ {
+ if (width_hint <= 0)
+ goto replace_fcall_later;
+
+ int num_steps = 1;
+
+ if (GetSize(children) != 1 && GetSize(children) != 2)
+ log_error("System function %s got %d arguments, expected 1 or 2 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+ if (!current_always_clocked)
+ log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+
+ if (GetSize(children) == 2)
+ {
+ AstNode *buf = children[1]->clone();
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (buf->type != AST_CONSTANT)
+ log_error("Failed to evaluate system function `%s' with non-constant value at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+
+ num_steps = buf->asInt(true);
+ delete buf;
+ }
+
+ AstNode *block = nullptr;
+
+ for (auto child : current_always->children)
+ if (child->type == AST_BLOCK)
+ block = child;
+
+ log_assert(block != nullptr);
+
+ int myidx = autoidx++;
+ AstNode *outreg = nullptr;
+
+ for (int i = 0; i < num_steps; i++)
+ {
+ AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE,
+ mkconst_int(width_hint-1, true), mkconst_int(0, true)));
+
+ reg->str = stringf("$past$%s:%d$%d$%d", filename.c_str(), linenum, myidx, i);
+ reg->is_reg = true;
+
+ current_ast_mod->children.push_back(reg);
+
+ while (reg->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *regid = new AstNode(AST_IDENTIFIER);
+ regid->str = reg->str;
+ regid->id2ast = reg;
+
+ AstNode *rhs = nullptr;
+
+ if (outreg == nullptr) {
+ rhs = children.at(0)->clone();
+ } else {
+ rhs = new AstNode(AST_IDENTIFIER);
+ rhs->str = outreg->str;
+ rhs->id2ast = outreg;
+ }
+
+ block->children.push_back(new AstNode(AST_ASSIGN_LE, regid, rhs));
+ outreg = reg;
+ }
+
+ newNode = new AstNode(AST_IDENTIFIER);
+ newNode->str = outreg->str;
+ newNode->id2ast = outreg;
+ goto apply_newNode;
+ }
+
+ if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell")
+ {
+ if (GetSize(children) != 1)
+ log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+ if (!current_always_clocked)
+ log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+
+ AstNode *present = children.at(0)->clone();
+ AstNode *past = clone();
+ past->str = "\\$past";
+
+ if (str == "\\$stable")
+ newNode = new AstNode(AST_EQ, past, present);
+
+ else if (str == "\\$rose")
+ newNode = new AstNode(AST_LOGIC_AND, new AstNode(AST_LOGIC_NOT, past), present);
+
+ else if (str == "\\$fell")
+ newNode = new AstNode(AST_LOGIC_AND, past, new AstNode(AST_LOGIC_NOT, present));
+
+ else
+ log_abort();
+
+ goto apply_newNode;
+ }
+
+ if (str == "\\$rose" || str == "\\$fell")
+ {
+ if (GetSize(children) != 1)
+ log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
+ if (!current_always_clocked)
+ log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+
+ newNode = new AstNode(AST_EQ, children.at(0)->clone(), clone());
+ newNode->children.at(1)->str = "\\$past";
+ goto apply_newNode;
+ }
+
+ // $anyconst is mapped in AstNode::genRTLIL()
+ if (str == "\\$anyconst") {
+ recursion_counter--;
+ return false;
+ }
+
if (str == "\\$clog2")
{
if (children.size() != 1)
@@ -1674,12 +1942,12 @@ skip_dynamic_range_lvalue_expansion:;
if (type == AST_TCALL)
{
- if (str == "$finish")
+ if (str == "$finish" || str == "$stop")
{
if (!current_always || current_always->type != AST_INITIAL)
- log_error("System task `$finish' outside initial block is unsupported at %s:%d.\n", filename.c_str(), linenum);
+ log_error("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
- log_error("System task `$finish' executed at %s:%d.\n", filename.c_str(), linenum);
+ log_error("System task `%s' executed at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
}
if (str == "\\$readmemh" || str == "\\$readmemb")
@@ -1889,6 +2157,8 @@ skip_dynamic_range_lvalue_expansion:;
wire->port_id = 0;
wire->is_input = false;
wire->is_output = false;
+ if (!child->is_output)
+ wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
wire_cache[child->str] = wire;
current_ast_mod->children.push_back(wire);
@@ -1949,6 +2219,8 @@ skip_dynamic_range_lvalue_expansion:;
did_something = true;
}
+replace_fcall_later:;
+
// perform const folding when activated
if (const_fold)
{
@@ -2347,12 +2619,12 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
block->children.back()->children[0]->id2ast = memory;
}
- if ((cursor == finish_addr) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
- break;
cursor += increment;
+ if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
+ break;
}
- if ((cursor == finish_addr) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
+ if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min))
break;
}
@@ -2568,16 +2840,54 @@ bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set)
return true;
}
+void AstNode::mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes)
+{
+ log_assert(mem2reg_set.count(this) == 0);
+
+ if (mem2reg_set.count(id2ast))
+ id2ast = nullptr;
+
+ for (size_t i = 0; i < children.size(); i++) {
+ if (mem2reg_set.count(children[i]) > 0) {
+ delnodes.push_back(children[i]);
+ children.erase(children.begin() + (i--));
+ } else {
+ children[i]->mem2reg_remove(mem2reg_set, delnodes);
+ }
+ }
+}
+
// actually replace memories with registers
-bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block)
+bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block)
{
bool did_something = false;
if (type == AST_BLOCK)
block = this;
- if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL &&
- children[0]->mem2reg_check(mem2reg_set) && children[0]->children[0]->children[0]->type != AST_CONSTANT)
+ if (type == AST_FUNCTION || type == AST_TASK)
+ return false;
+
+ if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set))
+ {
+ if (async_block == NULL) {
+ async_block = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
+ mod->children.push_back(async_block);
+ }
+
+ AstNode *newNode = clone();
+ newNode->type = AST_ASSIGN_EQ;
+ async_block->children[0]->children.push_back(newNode);
+
+ newNode = new AstNode(AST_NONE);
+ newNode->cloneInto(this);
+ delete newNode;
+
+ did_something = true;
+ }
+
+ if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->mem2reg_check(mem2reg_set) &&
+ children[0]->children[0]->children[0]->type != AST_CONSTANT)
{
std::stringstream sstr;
sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
@@ -2653,7 +2963,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
else
{
std::stringstream sstr;
- sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
+ sstr << "$mem2reg_rd$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++);
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
int mem_width, mem_size, addr_bits;
@@ -2733,7 +3043,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
auto children_list = children;
for (size_t i = 0; i < children_list.size(); i++)
- if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block))
+ if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block, async_block))
did_something = true;
return did_something;
@@ -2958,7 +3268,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
for (size_t i = 1; i < stmt->children.size(); i++)
{
bool found_match = false;
- log_assert(stmt->children.at(i)->type == AST_COND);
+ log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ);
if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) {
sel_case = stmt->children.at(i)->children.back();