From 9b183539af4adf7d2a127042ca384806c7e73367 Mon Sep 17 00:00:00 2001
From: Clifford Wolf <clifford@clifford.at>
Date: Thu, 17 Jul 2014 16:49:23 +0200
Subject: [PATCH] Implemented dynamic bit-/part-select for memory writes

---
 frontends/ast/simplify.cc | 28 ++++++++++++++++++++++++--
 tests/simple/memory.v     | 41 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index eee5a7b39..f819b2506 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1159,7 +1159,7 @@ skip_dynamic_range_lvalue_expansion:;
 		std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";
 
 		if (type == AST_ASSIGN_EQ)
-			log("Warining: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n",
+			log("Warning: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n",
 					filename.c_str(), linenum);
 
 		int mem_width, mem_size, addr_bits;
@@ -1230,7 +1230,31 @@ skip_dynamic_range_lvalue_expansion:;
 			}
 			else
 			{
-				log_error("Writing to memories with dynamic bit- or part-select is not supported yet at %s:%d.\n", filename.c_str(), linenum);
+				AstNode *the_range = children[0]->children[1];
+				AstNode *left_at_zero_ast = the_range->children[0]->clone();
+				AstNode *right_at_zero_ast = the_range->children.size() >= 2 ? the_range->children[1]->clone() : left_at_zero_ast->clone();
+				AstNode *offset_ast = right_at_zero_ast->clone();
+
+				while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+				while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+				if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
+					log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+				int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+
+				for (int i = 0; i < mem_width; i++)
+					set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
+
+				assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+						new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
+				assign_data->children[0]->str = id_data;
+
+				assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+						new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
+				assign_en->children[0]->str = id_en;
+
+				delete left_at_zero_ast;
+				delete right_at_zero_ast;
+				delete offset_ast;
 			}
 		}
 		else
diff --git a/tests/simple/memory.v b/tests/simple/memory.v
index 21271b5e2..ae63e8a16 100644
--- a/tests/simple/memory.v
+++ b/tests/simple/memory.v
@@ -137,7 +137,26 @@ endmodule
 
 // ----------------------------------------------------------
 
-module test06(input clk, input rst, input [2:0] idx, input [7:0] din, output [7:0] dout);
+module test06_sync(input clk, input rst, input [2:0] idx, input [7:0] din, output [7:0] dout);
+    (* gentb_constant=0 *) wire rst;
+    reg [7:0] test [0:7];
+    integer i;
+    always @(posedge clk) begin
+        if (rst) begin
+            for (i=0; i<8; i=i+1)
+                test[i] <= 0;
+        end else begin
+            test[0][2] <= din[1];
+            test[0][5] <= test[0][2];
+            test[idx][3] <= din[idx];
+            test[idx][6] <= test[idx][2];
+            test[idx][idx] <= !test[idx][idx];
+        end
+    end
+    assign dout = test[idx];
+endmodule
+
+module test06_async(input clk, input rst, input [2:0] idx, input [7:0] din, output [7:0] dout);
     (* gentb_constant=0 *) wire rst;
     reg [7:0] test [0:7];
     integer i;
@@ -155,3 +174,23 @@ module test06(input clk, input rst, input [2:0] idx, input [7:0] din, output [7:
     end
     assign dout = test[idx];
 endmodule
+
+// ----------------------------------------------------------
+
+module test07(clk, addr, woffset, wdata, rdata);
+
+input clk;
+input [1:0] addr;
+input [3:0] wdata;
+input [1:0] woffset;
+output reg [7:0] rdata;
+
+reg [7:0] mem [0:3];
+
+integer i;
+always @(posedge clk) begin
+	mem[addr][woffset +: 4] <= wdata;
+	rdata <= mem[addr];
+end
+
+endmodule