From e446e651cbd3373f1dc1fc79684c403707359d14 Mon Sep 17 00:00:00 2001
From: Andrew Zonenberg <azonenberg@drawersteak.com>
Date: Thu, 17 Sep 2015 20:34:56 -0700
Subject: Initial implementation of $finish()

---
 frontends/ast/simplify.cc | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index e588df922..76c9d5c51 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -174,7 +174,7 @@ 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" ||
+       if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || 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();
@@ -1569,7 +1569,13 @@ 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")
+			{
+				log_error("System task `$finish() executed in `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+			}
+
 			if (str == "\\$readmemh" || str == "\\$readmemb")
 			{
 				if (GetSize(children) < 2 || GetSize(children) > 4)
-- 
cgit v1.2.3


From 7141f655337fe2bcedba6029cc30f0a213c79810 Mon Sep 17 00:00:00 2001
From: Andrew Zonenberg <azonenberg@drawersteak.com>
Date: Thu, 17 Sep 2015 20:34:56 -0700
Subject: Initial implementation of $display()

---
 frontends/ast/simplify.cc | 85 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 84 insertions(+), 1 deletion(-)

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;
-- 
cgit v1.2.3


From 9db05d17feddf7615bb8944390fa7b535cd48b12 Mon Sep 17 00:00:00 2001
From: Clifford Wolf <clifford@clifford.at>
Date: Fri, 18 Sep 2015 09:50:53 +0200
Subject: Added AST_INITIAL checks for $finish and $display

---
 frontends/ast/simplify.cc | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 144fd41d0..dd4f0797e 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -187,10 +187,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")))
 	{
+		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",
+			log_error("System task `$display' got %d arguments, expected >= 1 at %s:%d.\n",
 				int(children.size()), filename.c_str(), linenum);
 		}
 
@@ -1652,11 +1655,15 @@ 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 (str == "$finish")
 			{
-				log_error("System task `$finish() executed in `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+				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")
-- 
cgit v1.2.3