Implemented part/bit select on memory read
authorClifford Wolf <clifford@clifford.at>
Wed, 20 Nov 2013 09:51:32 +0000 (10:51 +0100)
committerClifford Wolf <clifford@clifford.at>
Wed, 20 Nov 2013 09:51:32 +0000 (10:51 +0100)
README
frontends/ast/simplify.cc
frontends/verilog/parser.y
tests/simple/memory.v

diff --git a/README b/README
index 002e55ff531b0636b35a1f3d2ffafd17dd4d0c1c..76b8a1226e54314edf81cc5367bf304354247752 100644 (file)
--- a/README
+++ b/README
@@ -293,7 +293,6 @@ Roadmap / Large-scale TODOs
 
 - Missing Verilog-2005 features to be implemented soon:
   - Fix corner cases with contextual name lookup
-  - Part select on memory read
   - Indexed part selects
 
 - Technology mapping for real-world applications
index 30f723691ebb9fdd0e4ca335c232f0e50eb6120d..3c59ef350b3ac28e7ac07bcc22073cfc076a9122 100644 (file)
@@ -470,6 +470,55 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                id2ast = current_scope[str];
        }
 
+       // split memory access with bit select to individual statements
+       if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE)
+       {
+               if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)
+                       log_error("Invalid bit-select on memory access at %s:%d!\n", filename.c_str(), linenum);
+
+               int mem_width, mem_size, addr_bits;
+               id2ast->meminfo(mem_width, mem_size, addr_bits);
+
+               std::stringstream sstr;
+               sstr << "$mem2bits$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+               std::string wire_id = sstr.str();
+
+               AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+               wire->str = wire_id;
+               if (current_block)
+                       wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+               current_ast_mod->children.push_back(wire);
+               while (wire->simplify(true, false, false, 1, -1, false)) { }
+
+               AstNode *data = clone();
+               delete data->children[1];
+               data->children.pop_back();
+
+               AstNode *assign = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), data);
+               assign->children[0]->str = wire_id;
+
+               if (current_block)
+               {
+                       size_t assign_idx = 0;
+                       while (assign_idx < current_block->children.size() && current_block->children[assign_idx] != current_block_child)
+                               assign_idx++;
+                       log_assert(assign_idx < current_block->children.size());
+                       current_block->children.insert(current_block->children.begin()+assign_idx, assign);
+                       wire->is_reg = true;
+               }
+               else
+               {
+                       AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK));
+                       proc->children[0]->children.push_back(assign);
+                       current_ast_mod->children.push_back(proc);
+               }
+
+               newNode = new AstNode(AST_IDENTIFIER, children[1]->clone());
+               newNode->str = wire_id;
+               newNode->id2ast = wire;
+               goto apply_newNode;
+       }
+
        // unroll for loops and generate-for blocks
        if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
        {
@@ -1281,6 +1330,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
 
                AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
                wire_addr->str = id_addr;
+               wire_addr->is_reg = true;
                if (block)
                        wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
                mod->children.push_back(wire_addr);
@@ -1288,6 +1338,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
 
                AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
                wire_data->str = id_data;
+               wire_data->is_reg = true;
                if (block)
                        wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
                mod->children.push_back(wire_data);
@@ -1328,8 +1379,6 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
                        assert(assign_idx < block->children.size());
                        block->children.insert(block->children.begin()+assign_idx, case_node);
                        block->children.insert(block->children.begin()+assign_idx, assign_addr);
-                       wire_addr->is_reg = true;
-                       wire_data->is_reg = true;
                }
                else
                {
index 24c84514f1b618911883851ea576739a50622f87..c4f386ce5c1df12e066bf8741327899d66c657ff 100644 (file)
@@ -105,7 +105,7 @@ static void free_attr(std::map<std::string, AstNode*> *al)
 %token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE
 %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED
 
-%type <ast> wire_type range expr basic_expr concat_list rvalue lvalue lvalue_concat_list
+%type <ast> wire_type range non_opt_range 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
@@ -330,7 +330,7 @@ wire_type_token:
                astbuf3->is_signed = true;
        };
 
-range:
+non_opt_range:
        '[' expr ':' expr ']' {
                $$ = new AstNode(AST_RANGE);
                $$->children.push_back($2);
@@ -339,6 +339,11 @@ range:
        '[' expr ']' {
                $$ = new AstNode(AST_RANGE);
                $$->children.push_back($2);
+       };
+
+range:
+       non_opt_range {
+               $$ = $1;
        } |
        /* empty */ {
                $$ = NULL;
@@ -893,6 +898,11 @@ rvalue:
                $$ = new AstNode(AST_IDENTIFIER, $2);
                $$->str = *$1;
                delete $1;
+       } |
+       hierarchical_id non_opt_range non_opt_range {
+               $$ = new AstNode(AST_IDENTIFIER, $2, $3);
+               $$->str = *$1;
+               delete $1;
        };
 
 lvalue:
index c25bcd92860227d76692ca6dcdd4d07afa7822e7..aea014a284213f30923bd87b0023ffd10ba65a09 100644 (file)
@@ -17,3 +17,44 @@ always @(posedge clk)
 
 endmodule
 
+// ----------------------------------------------------------
+
+module test02(clk, setA, setB, addr, bit, y1, y2, y3, y4);
+
+input clk, setA, setB;
+input [1:0] addr;
+input [2:0] bit;
+output reg y1, y2;
+output y3, y4;
+
+reg [7:0] mem1 [3:0];
+
+(* mem2reg *)
+reg [7:0] mem2 [3:0];
+
+always @(posedge clk) begin
+       if (setA) begin
+               mem1[0] <= 10;
+               mem1[1] <= 20;
+               mem1[2] <= 30;
+               mem2[0] <= 17;
+               mem2[1] <= 27;
+               mem2[2] <= 37;
+       end
+       if (setB) begin
+               mem1[0] <=  1;
+               mem1[1] <=  2;
+               mem1[2] <=  3;
+               mem2[0] <= 71;
+               mem2[1] <= 72;
+               mem2[2] <= 73;
+       end
+       y1 <= mem1[addr][bit];
+       y2 <= mem2[addr][bit];
+end
+
+assign y3 = mem1[addr][bit];
+assign y4 = mem2[addr][bit];
+
+endmodule
+