diff options
author | Clifford Wolf <clifford@clifford.at> | 2013-11-21 13:49:00 +0100 |
---|---|---|
committer | Clifford Wolf <clifford@clifford.at> | 2013-11-21 13:49:00 +0100 |
commit | 09471846c553855c43224ce32d855c46f4df5140 (patch) | |
tree | 659fff2b882e2d7c54a5eaf72adeed2328d4ba64 /frontends/ast | |
parent | 84ced2bb8ee2e6498b53ae6cdb77930aa98affbb (diff) | |
download | yosys-09471846c553855c43224ce32d855c46f4df5140.tar.gz yosys-09471846c553855c43224ce32d855c46f4df5140.tar.bz2 yosys-09471846c553855c43224ce32d855c46f4df5140.zip |
Major improvements in mem2reg and added "init" sync rules
Diffstat (limited to 'frontends/ast')
-rw-r--r-- | frontends/ast/ast.h | 21 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 33 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 280 |
3 files changed, 247 insertions, 87 deletions
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 37f75454c..de32a2bb9 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -162,12 +162,31 @@ namespace AST void delete_children(); ~AstNode(); + enum mem2reg_flags + { + /* status flags */ + MEM2REG_FL_ALL = 0x00000001, + MEM2REG_FL_ASYNC = 0x00000002, + MEM2REG_FL_INIT = 0x00000004, + + /* candidate flags */ + MEM2REG_FL_FORCED = 0x00000100, + MEM2REG_FL_SET_INIT = 0x00000200, + MEM2REG_FL_SET_ELSE = 0x00000400, + MEM2REG_FL_SET_ASYNC = 0x00000800, + MEM2REG_FL_EQ2 = 0x00001000, + + /* proc flags */ + MEM2REG_FL_EQ1 = 0x01000000, + }; + // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc. // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint); void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map); void replace_ids(std::map<std::string, std::string> &rules); - void mem2reg_as_needed_pass1(std::set<AstNode*> &mem2reg_set, std::set<AstNode*> &mem2reg_candidates, bool sync_proc, bool async_proc, bool force_mem2reg); + void mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> &mem2reg_places, + std::map<AstNode*, uint32_t> &mem2reg_flags, std::map<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags); void mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block); void meminfo(int &mem_width, int &mem_size, int &addr_bits); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 550f7245e..e634a27a9 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -234,7 +234,7 @@ struct AST_INTERNAL::ProcessGenerator { // input and output structures AstNode *always; - RTLIL::SigSpec skipSyncSignals; + RTLIL::SigSpec initSyncSignals; RTLIL::Process *proc; const RTLIL::SigSpec &outputSignals; @@ -258,7 +258,10 @@ struct AST_INTERNAL::ProcessGenerator // map helps generating nice numbered names for all this temporary signals. std::map<RTLIL::Wire*, int> new_temp_count; - ProcessGenerator(AstNode *always, RTLIL::SigSpec skipSyncSignalsArg = RTLIL::SigSpec()) : always(always), skipSyncSignals(skipSyncSignalsArg), outputSignals(subst_lvalue_from) + // Buffer for generating the init action + RTLIL::SigSpec init_lvalue, init_rvalue; + + ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), outputSignals(subst_lvalue_from) { // generate process and simple root case proc = new RTLIL::Process; @@ -321,6 +324,25 @@ struct AST_INTERNAL::ProcessGenerator for (auto child : always->children) if (child->type == AST_BLOCK) processAst(child); + + if (initSyncSignals.width > 0) + { + RTLIL::SyncRule *sync = new RTLIL::SyncRule; + sync->type = RTLIL::SyncType::STi; + proc->syncs.push_back(sync); + + assert(init_lvalue.width == init_rvalue.width); + init_lvalue.optimize(); + init_rvalue.optimize(); + + int offset = 0; + for (size_t i = 0; i < init_lvalue.chunks.size(); i++) { + RTLIL::SigSpec lhs = init_lvalue.chunks[i]; + RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue.chunks[i].width); + sync->actions.push_back(RTLIL::SigSig(lhs, rhs)); + offset += lhs.width; + } + } } // create new temporary signals @@ -406,8 +428,11 @@ struct AST_INTERNAL::ProcessGenerator // are avoided and the generated $mux cells have a more "natural" size. void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false) { - if (inSyncRule) - lvalue.remove2(skipSyncSignals, &rvalue); + if (inSyncRule && initSyncSignals.width > 0) { + init_lvalue.append(lvalue.extract(initSyncSignals)); + init_rvalue.append(lvalue.extract(initSyncSignals, &rvalue)); + lvalue.remove2(initSyncSignals, &rvalue); + } assert(lvalue.width == rvalue.width); lvalue.optimize(); rvalue.optimize(); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 636dde48e..f1cce397c 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -56,8 +56,46 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg")) { - std::set<AstNode*> mem2reg_set, mem2reg_candidates; - mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, false, false, flag_mem2reg); + std::map<AstNode*, std::set<std::string>> mem2reg_places; + std::map<AstNode*, uint32_t> mem2reg_candidates, dummy_proc_flags; + uint32_t flags = flag_mem2reg ? AstNode::MEM2REG_FL_ALL : 0; + mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, dummy_proc_flags, flags); + + std::set<AstNode*> mem2reg_set; + for (auto &it : mem2reg_candidates) + { + AstNode *mem = it.first; + uint32_t memflags = it.second; + assert((memflags & ~0x00ffff00) == 0); + + if (mem->get_bool_attribute("\\nomem2reg")) + continue; + + if (memflags & AstNode::MEM2REG_FL_FORCED) + goto silent_activate; + + if (memflags & AstNode::MEM2REG_FL_EQ2) + goto verbose_activate; + + if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE)) + goto verbose_activate; + + continue; + + verbose_activate: + if (mem2reg_set.count(mem) == 0) { + log("Warning: Replacing memory %s with list of registers.", mem->str.c_str()); + bool first_element = true; + for (auto &place : mem2reg_places[it.first]) { + log("%s%s", first_element ? " See " : ", ", place.c_str()); + first_element = false; + } + log("\n"); + } + + silent_activate: + mem2reg_set.insert(mem); + } for (auto node : mem2reg_set) { @@ -1249,36 +1287,102 @@ void AstNode::replace_ids(std::map<std::string, std::string> &rules) } // find memories that should be replaced by registers -void AstNode::mem2reg_as_needed_pass1(std::set<AstNode*> &mem2reg_set, std::set<AstNode*> &mem2reg_candidates, bool sync_proc, bool async_proc, bool force_mem2reg) +void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> &mem2reg_places, + std::map<AstNode*, uint32_t> &mem2reg_candidates, std::map<AstNode*, uint32_t> &proc_flags, uint32_t &flags) { - if ((type == AST_ASSIGN_LE && async_proc) || (type == AST_ASSIGN_EQ && (sync_proc || async_proc))) - if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && - !children[0]->id2ast->get_bool_attribute("\\nomem2reg")) { - if (async_proc || mem2reg_candidates.count(children[0]->id2ast) > 0) { - if (mem2reg_set.count(children[0]->id2ast) == 0) - log("Warning: Replacing memory %s with list of registers because of assignment in line %s:%d.\n", - children[0]->str.c_str(), filename.c_str(), linenum); - mem2reg_set.insert(children[0]->id2ast); + uint32_t children_flags = 0; + int ignore_children_counter = 0; + + if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) + { + if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY) + { + AstNode *mem = children[0]->id2ast; + + // activate mem2reg if this is assigned in an async proc + if (flags & AstNode::MEM2REG_FL_ASYNC) { + if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC)) + mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum)); + mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ASYNC; + } + + // remember if this is assigned blocking (=) + if (type == AST_ASSIGN_EQ) { + if (!(proc_flags[mem] & AstNode::MEM2REG_FL_EQ1)) + mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum)); + proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1; + } + + // remember where this is + if (flags & MEM2REG_FL_INIT) { + if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT)) + mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum)); + mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT; + } else { + if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE)) + mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum)); + mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE; } - mem2reg_candidates.insert(children[0]->id2ast); } - - if (type == AST_MEMORY && (get_bool_attribute("\\mem2reg") || force_mem2reg)) - mem2reg_set.insert(this); + + ignore_children_counter = 1; + } + + if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY) + { + AstNode *mem = id2ast; + + // flag if used after blocking assignment (in same proc) + if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) { + mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), linenum)); + mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2; + } + } + + // also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg' + if (type == AST_MEMORY && (get_bool_attribute("\\mem2reg") || (flags & AstNode::MEM2REG_FL_ALL) || !is_reg)) + mem2reg_candidates[this] |= AstNode::MEM2REG_FL_FORCED; if (type == AST_MODULE && get_bool_attribute("\\mem2reg")) - force_mem2reg = true; + children_flags |= AstNode::MEM2REG_FL_ALL; + + std::map<AstNode*, uint32_t> *proc_flags_p = NULL; if (type == AST_ALWAYS) { + bool sync_proc = false; for (auto child : children) { if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) sync_proc = true; } - async_proc = !sync_proc; + if (!sync_proc) + children_flags |= AstNode::MEM2REG_FL_ASYNC; + proc_flags_p = new std::map<AstNode*, uint32_t>; + } + + if (type == AST_INITIAL) { + children_flags |= AstNode::MEM2REG_FL_INIT; + proc_flags_p = new std::map<AstNode*, uint32_t>; } + uint32_t backup_flags = flags; + flags |= children_flags; + assert((flags & ~0x000000ff) == 0); + for (auto child : children) - child->mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, sync_proc, async_proc, force_mem2reg); + if (ignore_children_counter > 0) + ignore_children_counter--; + else if (proc_flags_p) + child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags); + else + child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags); + + flags &= ~children_flags | backup_flags; + + if (proc_flags_p) { + for (auto it : *proc_flags_p) + assert((it.second & ~0xff000000) == 0); + delete proc_flags_p; + } } // actually replace memories with registers @@ -1287,8 +1391,8 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * if (type == AST_BLOCK) block = this; - if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL && - children[0]->id2ast && mem2reg_set.count(children[0]->id2ast) > 0) + if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL && children[0]->id2ast && + mem2reg_set.count(children[0]->id2ast) > 0 && children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); @@ -1344,77 +1448,89 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0) { - std::stringstream sstr; - sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); - std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; - - int mem_width, mem_size, addr_bits; - id2ast->meminfo(mem_width, mem_size, addr_bits); - - 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); - while (wire_addr->simplify(true, false, false, 1, -1, false)) { } - - 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); - while (wire_data->simplify(true, false, false, 1, -1, false)) { } + if (children[0]->children[0]->type == AST_CONSTANT) + { + int id = children[0]->children[0]->integer; + str = stringf("%s[%d]", str.c_str(), id); - AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone()); - assign_addr->children[0]->str = id_addr; + delete_children(); + range_valid = false; + id2ast = NULL; + } + else + { + std::stringstream sstr; + sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; + + int mem_width, mem_size, addr_bits; + id2ast->meminfo(mem_width, mem_size, addr_bits); + + 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); + while (wire_addr->simplify(true, false, false, 1, -1, false)) { } + + 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); + while (wire_data->simplify(true, false, false, 1, -1, false)) { } + + AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone()); + assign_addr->children[0]->str = id_addr; + + AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); + case_node->children[0]->str = id_addr; + + for (int i = 0; i < mem_size; i++) { + if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i) + continue; + AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); + AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); + assign_reg->children[0]->str = id_data; + assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i); + cond_node->children[1]->children.push_back(assign_reg); + case_node->children.push_back(cond_node); + } - AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); - case_node->children[0]->str = id_addr; + std::vector<RTLIL::State> x_bits; + for (int i = 0; i < mem_width; i++) + x_bits.push_back(RTLIL::State::Sx); - for (int i = 0; i < mem_size; i++) { - if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i) - continue; - AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); - AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); + AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK)); + AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false)); assign_reg->children[0]->str = id_data; - assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i); cond_node->children[1]->children.push_back(assign_reg); case_node->children.push_back(cond_node); - } - - std::vector<RTLIL::State> x_bits; - for (int i = 0; i < mem_width; i++) - x_bits.push_back(RTLIL::State::Sx); - AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK)); - AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false)); - assign_reg->children[0]->str = id_data; - cond_node->children[1]->children.push_back(assign_reg); - case_node->children.push_back(cond_node); + if (block) + { + size_t assign_idx = 0; + while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this)) + assign_idx++; + 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); + } + else + { + AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); + proc->children[0]->children.push_back(case_node); + mod->children.push_back(proc); + mod->children.push_back(assign_addr); + } - if (block) - { - size_t assign_idx = 0; - while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this)) - assign_idx++; - 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); - } - else - { - AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); - proc->children[0]->children.push_back(case_node); - mod->children.push_back(proc); - mod->children.push_back(assign_addr); + delete_children(); + range_valid = false; + id2ast = NULL; + str = id_data; } - - delete_children(); - range_valid = false; - id2ast = NULL; - str = id_data; } assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); |