diff options
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 53 | ||||
-rw-r--r-- | frontends/verilog/parser.y | 14 | ||||
-rw-r--r-- | tests/simple/memory.v | 41 |
4 files changed, 104 insertions, 5 deletions
@@ -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 diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 30f723691..3c59ef350 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -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 { diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y index 24c84514f..c4f386ce5 100644 --- a/frontends/verilog/parser.y +++ b/frontends/verilog/parser.y @@ -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: diff --git a/tests/simple/memory.v b/tests/simple/memory.v index c25bcd928..aea014a28 100644 --- a/tests/simple/memory.v +++ b/tests/simple/memory.v @@ -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 + |