Initial implementation of $display()
authorAndrew Zonenberg <azonenberg@drawersteak.com>
Fri, 18 Sep 2015 03:34:56 +0000 (20:34 -0700)
committerClifford Wolf <clifford@clifford.at>
Fri, 18 Sep 2015 07:36:46 +0000 (09:36 +0200)
frontends/ast/simplify.cc

index 76c9d5c51a16f733a3bd3f2a7d997d924fd5a12e..144fd41d096b772f066f1369da90da489eba7075 100644 (file)
@@ -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;