diff options
Diffstat (limited to 'frontends')
-rw-r--r-- | frontends/ast/simplify.cc | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 76c9d5c51..144fd41d0 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -174,13 +174,96 @@ 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" || + // 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"))) + { + 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; |