From d259abbda2b9d568228dc8d0bed2d0b0d88d7b4f Mon Sep 17 00:00:00 2001
From: Clifford Wolf <clifford@clifford.at>
Date: Wed, 6 Aug 2014 15:43:46 +0200
Subject: [PATCH] Added AST_MULTIRANGE (arrays with more than 1 dimension)

---
 README                     |  1 -
 frontends/ast/ast.cc       |  7 +++++
 frontends/ast/ast.h        |  4 +++
 frontends/ast/simplify.cc  | 52 +++++++++++++++++++++++++++++++++++++-
 frontends/verilog/parser.y | 22 +++++++++++++---
 5 files changed, 80 insertions(+), 6 deletions(-)

diff --git a/README b/README
index 63d692297..0c8425f37 100644
--- a/README
+++ b/README
@@ -326,7 +326,6 @@ Other Unsorted TODOs
 
 - Implement missing Verilog 2005 features:
 
-  - Multi-dimensional arrays
   - Support for real (float) const. expressions and parameters
   - ROM modeling using $readmemh/$readmemb in "initial" blocks
   - Ignore what needs to be ignored (e.g. drive and charge strengths)
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 5815fb0d4..f18124e28 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -78,6 +78,7 @@ std::string AST::type2str(AstNodeType type)
 	X(AST_PARASET)
 	X(AST_ARGUMENT)
 	X(AST_RANGE)
+	X(AST_MULTIRANGE)
 	X(AST_CONSTANT)
 	X(AST_REALVALUE)
 	X(AST_CELLTYPE)
@@ -284,6 +285,12 @@ void AstNode::dumpAst(FILE *f, std::string indent)
 		fprintf(f, " int=%u", (int)integer);
 	if (realvalue != 0)
 		fprintf(f, " real=%e", realvalue);
+	if (!multirange_dimensions.empty()) {
+		fprintf(f, " multirange=[");
+		for (int v : multirange_dimensions)
+			fprintf(f, " %d", v);
+		fprintf(f, " ]");
+	}
 	fprintf(f, "\n");
 
 	for (auto &it : attributes) {
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 00b044bc4..5fb1f0a73 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -56,6 +56,7 @@ namespace AST
 		AST_PARASET,
 		AST_ARGUMENT,
 		AST_RANGE,
+		AST_MULTIRANGE,
 		AST_CONSTANT,
 		AST_REALVALUE,
 		AST_CELLTYPE,
@@ -158,6 +159,9 @@ namespace AST
 		uint32_t integer;
 		double realvalue;
 
+		// if this is a multirange memory then this vector contains offset and length of each dimension
+		std::vector<int> multirange_dimensions;
+
 		// this is set by simplify and used during RTLIL generation
 		AstNode *id2ast;
 
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 29d00be96..39c472621 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -553,6 +553,56 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 		}
 	}
 
+	// resolve multiranges on memory decl
+	if (type == AST_MEMORY && children.size() > 1 && children[1]->type == AST_MULTIRANGE)
+	{
+		int total_size = 1;
+		multirange_dimensions.clear();
+		for (auto range : children[1]->children) {
+			if (!range->range_valid)
+				log_error("Non-constant range on memory decl at %s:%d.\n", filename.c_str(), linenum);
+			multirange_dimensions.push_back(std::min(range->range_left, range->range_right));
+			multirange_dimensions.push_back(std::max(range->range_left, range->range_right) - std::min(range->range_left, range->range_right) + 1);
+			total_size *= multirange_dimensions.back();
+		}
+		delete children[1];
+		children[1] = new AstNode(AST_RANGE, AstNode::mkconst_int(0, true), AstNode::mkconst_int(total_size-1, true));
+		did_something = true;
+	}
+
+	// resolve multiranges on memory access
+	if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY && children.size() > 0 && children[0]->type == AST_MULTIRANGE)
+	{
+		AstNode *index_expr = nullptr;
+
+		for (int i = 0; 2*i < SIZE(id2ast->multirange_dimensions); i++)
+		{
+			if (SIZE(children[0]->children) < i)
+				log_error("Insufficient number of array indices for %s at %s:%d.\n", log_id(str), filename.c_str(), linenum);
+
+			AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone();
+
+			if (id2ast->multirange_dimensions[2*i])
+				new_index_expr = new AstNode(AST_SUB, new_index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i], true));
+
+			if (i == 0)
+				index_expr = new_index_expr;
+			else
+				index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i-1], true)), new_index_expr);
+		}
+
+		for (int i = SIZE(id2ast->multirange_dimensions)/1; i < SIZE(children[0]->children); i++)
+			children.push_back(children[0]->children[i]->clone());
+
+		delete children[0];
+		if (index_expr == nullptr)
+			children.erase(children.begin());
+		else
+			children[0] = new AstNode(AST_RANGE, index_expr);
+
+		did_something = true;
+	}
+
 	// trim/extend parameters
 	if (type == AST_PARAMETER || type == AST_LOCALPARAM) {
 		if (children.size() > 1 && children[1]->type == AST_RANGE) {
@@ -1166,7 +1216,7 @@ skip_dynamic_range_lvalue_expansion:;
 	if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
 			children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 &&
 			children[0]->id2ast->children[0]->range_valid && children[0]->id2ast->children[1]->range_valid &&
-			(children[0]->children.size() == 1 || children[0]->children.size() == 2))
+			(children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)
 	{
 		std::stringstream sstr;
 		sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y
index 26e2ddc34..95d7f3935 100644
--- a/frontends/verilog/parser.y
+++ b/frontends/verilog/parser.y
@@ -112,7 +112,8 @@ static void free_attr(std::map<std::string, AstNode*> *al)
 %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED
 %token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_PROPERTY
 
-%type <ast> wire_type range non_opt_range range_or_signed_int expr basic_expr concat_list rvalue lvalue lvalue_concat_list
+%type <ast> range range_or_multirange  non_opt_range non_opt_multirange range_or_signed_int
+%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
 %type <string> opt_label tok_prim_wrapper hierarchical_id
 %type <boolean> opt_signed
 %type <al> attr
@@ -361,6 +362,15 @@ non_opt_range:
 		$$->children.push_back($2);
 	};
 
+non_opt_multirange:
+	non_opt_range non_opt_range {
+		$$ = new AstNode(AST_MULTIRANGE, $1, $2);
+	} |
+	non_opt_multirange non_opt_range {
+		$$ = $1;
+		$$->children.push_back($2);
+	};
+
 range:
 	non_opt_range {
 		$$ = $1;
@@ -369,6 +379,10 @@ range:
 		$$ = NULL;
 	};
 
+range_or_multirange:
+	range { $$ = $1; } |
+	non_opt_multirange { $$ = $1; };
+
 range_or_signed_int:
 	range {
 		$$ = $1;
@@ -566,7 +580,7 @@ wire_name_and_opt_assign:
 	};
 
 wire_name:
-	TOK_ID range {
+	TOK_ID range_or_multirange {
 		AstNode *node = astbuf1->clone();
 		node->str = *$1;
 		append_attr_clone(node, albuf);
@@ -1007,8 +1021,8 @@ rvalue:
 		$$->str = *$1;
 		delete $1;
 	} |
-	hierarchical_id non_opt_range non_opt_range {
-		$$ = new AstNode(AST_IDENTIFIER, $2, $3);
+	hierarchical_id non_opt_multirange {
+		$$ = new AstNode(AST_IDENTIFIER, $2);
 		$$->str = *$1;
 		delete $1;
 	};