ast: Add support for $sformatf system function
authorDavid Shah <dave@ds0.me>
Sun, 19 Jan 2020 21:15:51 +0000 (21:15 +0000)
committerDavid Shah <dave@ds0.me>
Sun, 19 Jan 2020 21:20:17 +0000 (21:20 +0000)
Signed-off-by: David Shah <dave@ds0.me>
frontends/ast/ast.h
frontends/ast/simplify.cc
tests/various/sformatf.ys [new file with mode: 0644]

index 918d178c7bfcc91ff1c813361e696a6b6323c54e..14e1cec5edf506b7c54a338d7867acae386bf715 100644 (file)
@@ -244,6 +244,7 @@ namespace AST
                void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall);
                AstNode *eval_const_function(AstNode *fcall);
                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);
 
                // create a human-readable text representation of the AST (for debugging)
                void dumpAst(FILE *f, std::string indent) const;
index b94a8d710fab73bb00bba33d765a911bd335e951..8855d9954bcf20a97bd69cc8ee0ebbf08884ee6e 100644 (file)
@@ -41,6 +41,103 @@ YOSYS_NAMESPACE_BEGIN
 using namespace AST;
 using namespace AST_INTERNAL;
 
+// Process a format string and arguments for $display, $write, $sprintf, etc
+
+std::string AstNode::process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint) {
+       // Other arguments are placeholders. Process the string as we go through it
+       std::string sout;
+       for (size_t i = 0; i < sformat.length(); i++)
+       {
+               // format specifier
+               if (sformat[i] == '%')
+               {
+                       // If there's no next character, that's a problem
+                       if (i+1 >= sformat.length())
+                               log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str());
+
+                       char cformat = sformat[++i];
+
+                       // %% is special, does not need a matching argument
+                       if (cformat == '%')
+                       {
+                               sout += '%';
+                               continue;
+                       }
+
+                       // Simplify the argument
+                       AstNode *node_arg = nullptr;
+
+                       // Everything from here on depends on the format specifier
+                       switch (cformat)
+                       {
+                               case 's':
+                               case 'S':
+                               case 'd':
+                               case 'D':
+                               case 'x':
+                               case 'X':
+                                       if (next_arg >= GetSize(children))
+                                               log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n",
+                                                               cformat, str.c_str());
+
+                                       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_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str());
+                                       break;
+
+                               case 'm':
+                               case 'M':
+                                       break;
+
+                               default:
+                                       log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str());
+                                       break;
+                       }
+
+                       switch (cformat)
+                       {
+                               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;
+
+                               case 'x':
+                               case 'X':
+                                       {
+                                               char tmp[128];
+                                               snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int());
+                                               sout += tmp;
+                                       }
+                                       break;
+
+                               case 'm':
+                               case 'M':
+                                       sout += log_id(current_module->name);
+                                       break;
+
+                               default:
+                                       log_abort();
+                       }
+               }
+
+               // not a format specifier
+               else
+                       sout += sformat[i];
+       }
+       return sout;
+}
+
+
 // 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().
@@ -216,99 +313,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                if (node_string->type != AST_CONSTANT)
                        log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str());
                std::string sformat = node_string->bitsAsConst().decode_string();
-
-               // Other arguments are placeholders. Process the string as we go through it
-               std::string sout;
-               int next_arg = 1;
-               for (size_t i = 0; i < sformat.length(); i++)
-               {
-                       // format specifier
-                       if (sformat[i] == '%')
-                       {
-                               // If there's no next character, that's a problem
-                               if (i+1 >= sformat.length())
-                                       log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str());
-
-                               char cformat = sformat[++i];
-
-                               // %% is special, does not need a matching argument
-                               if (cformat == '%')
-                               {
-                                       sout += '%';
-                                       continue;
-                               }
-
-                               // Simplify the argument
-                               AstNode *node_arg = nullptr;
-
-                               // Everything from here on depends on the format specifier
-                               switch (cformat)
-                               {
-                                       case 's':
-                                       case 'S':
-                                       case 'd':
-                                       case 'D':
-                                       case 'x':
-                                       case 'X':
-                                               if (next_arg >= GetSize(children))
-                                                       log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n",
-                                                                       cformat, str.c_str());
-
-                                               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_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str());
-                                               break;
-
-                                       case 'm':
-                                       case 'M':
-                                               break;
-
-                                       default:
-                                               log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str());
-                                               break;
-                               }
-
-                               switch (cformat)
-                               {
-                                       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;
-
-                                       case 'x':
-                                       case 'X':
-                                               {
-                                                       char tmp[128];
-                                                       snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int());
-                                                       sout += tmp;
-                                               }
-                                               break;
-
-                                       case 'm':
-                                       case 'M':
-                                               sout += log_id(current_module->name);
-                                               break;
-
-                                       default:
-                                               log_abort();
-                               }
-                       }
-
-                       // not a format specifier
-                       else
-                               sout += sformat[i];
-               }
-
+               std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint);
                // Finally, print the message (only include a \n for $display, not for $write)
                log("%s", sout.c_str());
                if (str == "$display")
@@ -2244,6 +2249,17 @@ skip_dynamic_range_lvalue_expansion:;
                                goto apply_newNode;
                        }
 
+                       if (str == "\\$sformatf") {
+                               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_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str());
+                               std::string sformat = node_string->bitsAsConst().decode_string();
+                               std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint);
+                               newNode = AstNode::mkconst_str(sout);
+                               goto apply_newNode;
+                       }
+
                        if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION)
                        {
                                AstNode *dpi_decl = current_scope[str];
diff --git a/tests/various/sformatf.ys b/tests/various/sformatf.ys
new file mode 100644 (file)
index 0000000..66d6b0d
--- /dev/null
@@ -0,0 +1,12 @@
+read_verilog <<EOT
+
+module top;
+       localparam a = $sformatf("0x%x", 8'h5A);
+       localparam b = $sformatf("%d", 4'b011);
+       generate
+               if (a != "0x5a") $error("a incorrect!");
+               if (b != "3") $error("b incorrect!");
+       endgenerate
+endmodule
+
+EOT