summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2015-09-18 09:54:49 +0200
committerClifford Wolf <clifford@clifford.at>2015-09-18 09:54:49 +0200
commit7a230d3a8dd6f19ab403cc831bf57ca31fc7f3da (patch)
tree638a93ba2e32fadb79b6f83f81c222743c5ba11f
parentd9cecabb877b1d58efc1bfcd962201eace67640d (diff)
parent9db05d17feddf7615bb8944390fa7b535cd48b12 (diff)
Merge branch 'feat-finish-disp'
-rw-r--r--frontends/ast/simplify.cc100
1 files changed, 98 insertions, 2 deletions
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index e588df92..dd4f0797 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -174,13 +174,99 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
// deactivate all calls to non-synthesis system tasks
- if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$strobe" || str == "$monitor" || str == "$time" || str == "$stop" || str == "$finish" ||
+ // 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" ||
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();
str = std::string();
}
+ // print messages if this a call to $display() or $write()
+ // This code implements only a small subset of Verilog-2005 $display() format specifiers,
+ // but should be good enough for most uses
+ if ((type == AST_TCALL) && ((str == "$display") || (str == "$write")))
+ {
+ if (!current_always || current_always->type != AST_INITIAL)
+ log_error("System task `$display' outside initial block is unsupported at %s:%d.\n", filename.c_str(), linenum);
+
+ size_t nargs = GetSize(children);
+ if(nargs < 1)
+ {
+ log_error("System task `$display' got %d arguments, expected >= 1 at %s:%d.\n",
+ int(children.size()), filename.c_str(), linenum);
+ }
+
+ // First argument is the format string
+ AstNode *node_string = children[0]->clone();
+ 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);
+ std::string sformat = node_string->bitsAsConst().decode_string();
+
+ // 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++)
+ {
+ // format specifier
+ if(sformat[i] == '%')
+ {
+ // 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);
+
+ // If there's no next character, that's a problem
+ 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);
+
+ // Everything from here on depends on the format specifier
+ char cformat = sformat[++i];
+ switch(cformat)
+ {
+ case '%':
+ sout += '%';
+ break;
+
+ case 's':
+ case 'S':
+ sout += node_arg->bitsAsConst().decode_string();
+ break;
+
+ case 'd':
+ case 'D':
+ {
+ char tmp[128];
+ snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int());
+ sout += tmp;
+ }
+ break;
+
+ default:
+ log_error("System task `%s' called with invalid format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ break;
+ }
+ }
+
+ // not a format specifier
+ else
+ sout += sformat[i];
+ }
+
+ // Finally, print the message (only include a \n for $display, not for $write)
+ log("%s", sout.c_str());
+ if(str == "$display")
+ log("\n");
+ delete_children();
+ str = std::string();
+ }
+
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX)
const_fold = true;
@@ -1569,7 +1655,17 @@ skip_dynamic_range_lvalue_expansion:;
if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION)
log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
}
- if (type == AST_TCALL) {
+
+ if (type == AST_TCALL)
+ {
+ if (str == "$finish")
+ {
+ 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 `$finish' executed at %s:%d.\n", filename.c_str(), linenum);
+ }
+
if (str == "\\$readmemh" || str == "\\$readmemb")
{
if (GetSize(children) < 2 || GetSize(children) > 4)