From: Clifford Wolf Date: Thu, 21 Nov 2013 12:49:00 +0000 (+0100) Subject: Major improvements in mem2reg and added "init" sync rules X-Git-Tag: yosys-0.2.0~343 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=09471846c553855c43224ce32d855c46f4df5140;p=yosys.git Major improvements in mem2reg and added "init" sync rules --- diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc index 9bf0fb0ae..513e57fa7 100644 --- a/backends/ilang/ilang_backend.cc +++ b/backends/ilang/ilang_backend.cc @@ -225,6 +225,7 @@ void ILANG_BACKEND::dump_proc_sync(FILE *f, std::string indent, const RTLIL::Syn fprintf(f, "\n"); break; case RTLIL::STa: fprintf(f, "always\n"); break; + case RTLIL::STi: fprintf(f, "init\n"); break; } for (auto it = sy->actions.begin(); it != sy->actions.end(); it++) { 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 &name_map); void replace_ids(std::map &rules); - void mem2reg_as_needed_pass1(std::set &mem2reg_set, std::set &mem2reg_candidates, bool sync_proc, bool async_proc, bool force_mem2reg); + void mem2reg_as_needed_pass1(std::map> &mem2reg_places, + std::map &mem2reg_flags, std::map &proc_flags, uint32_t &status_flags); void mem2reg_as_needed_pass2(std::set &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 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 &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 mem2reg_set, mem2reg_candidates; - mem2reg_as_needed_pass1(mem2reg_set, mem2reg_candidates, false, false, flag_mem2reg); + std::map> mem2reg_places; + std::map 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 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 &rules) } // find memories that should be replaced by registers -void AstNode::mem2reg_as_needed_pass1(std::set &mem2reg_set, std::set &mem2reg_candidates, bool sync_proc, bool async_proc, bool force_mem2reg) +void AstNode::mem2reg_as_needed_pass1(std::map> &mem2reg_places, + std::map &mem2reg_candidates, std::map &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 *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; + } + + if (type == AST_INITIAL) { + children_flags |= AstNode::MEM2REG_FL_INIT; + proc_flags_p = new std::map; } + 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 &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 &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 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 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); diff --git a/frontends/ilang/lexer.l b/frontends/ilang/lexer.l index 2a20f02a8..73bc894b1 100644 --- a/frontends/ilang/lexer.l +++ b/frontends/ilang/lexer.l @@ -60,6 +60,7 @@ "negedge" { return TOK_NEGEDGE; } "edge" { return TOK_EDGE; } "always" { return TOK_ALWAYS; } +"init" { return TOK_INIT; } "update" { return TOK_UPDATE; } "process" { return TOK_PROCESS; } "end" { return TOK_END; } diff --git a/frontends/ilang/parser.y b/frontends/ilang/parser.y index b889a9989..dc39cf93f 100644 --- a/frontends/ilang/parser.y +++ b/frontends/ilang/parser.y @@ -52,7 +52,7 @@ using namespace ILANG_FRONTEND; %token TOK_INT %token TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT %token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC -%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS +%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_INIT %token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET %token TOK_PARAMETER TOK_ATTRIBUTE TOK_AUTO TOK_MEMORY TOK_SIZE @@ -279,6 +279,12 @@ sync_list: rule->signal = RTLIL::SigSpec(); current_process->syncs.push_back(rule); } update_list | + sync_list TOK_SYNC TOK_INIT TOK_EOL { + RTLIL::SyncRule *rule = new RTLIL::SyncRule; + rule->type = RTLIL::SyncType::STi; + rule->signal = RTLIL::SigSpec(); + current_process->syncs.push_back(rule); + } update_list | /* empty */; sync_type: diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 952cb5944..c8f6b370f 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -44,7 +44,8 @@ namespace RTLIL STp = 2, // edge sensitive: posedge STn = 3, // edge sensitive: negedge STe = 4, // edge sensitive: both edges - STa = 5 // always active + STa = 5, // always active + STi = 6 // init }; extern int autoidx; diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index 68564e055..dfbc78eae 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -2,6 +2,7 @@ OBJS += passes/proc/proc.o OBJS += passes/proc/proc_clean.o OBJS += passes/proc/proc_rmdead.o +OBJS += passes/proc/proc_init.o OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o OBJS += passes/proc/proc_dff.o diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index eb99ab005..36d4141bb 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -34,6 +34,7 @@ struct ProcPass : public Pass { log("\n"); log(" proc_clean\n"); log(" proc_rmdead\n"); + log(" proc_init\n"); log(" proc_arst\n"); log(" proc_mux\n"); log(" proc_dff\n"); @@ -67,6 +68,7 @@ struct ProcPass : public Pass { Pass::call(design, "proc_clean"); Pass::call(design, "proc_rmdead"); + Pass::call(design, "proc_init"); if (global_arst.empty()) Pass::call(design, "proc_arst"); else diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc new file mode 100644 index 000000000..fca20fda2 --- /dev/null +++ b/passes/proc/proc_init.cc @@ -0,0 +1,114 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include +#include + +static void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule) +{ + assert(rule.compare.size() == 0); + + while (1) { + sig.optimize(); + RTLIL::SigSpec tmp = sig; + for (auto &it : rule.actions) + tmp.replace(it.first, it.second); + if (tmp == sig) + break; + sig = tmp; + } +} + +static void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) +{ + bool found_init = false; + + for (auto &sync : proc->syncs) + if (sync->type == RTLIL::SyncType::STi) + { + found_init = true; + log("Found init rule in `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); + + for (auto &action : sync->actions) + { + RTLIL::SigSpec lhs = action.first; + RTLIL::SigSpec rhs = action.second; + + lhs.optimize(); + proc_get_const(rhs, proc->root_case); + + if (!rhs.is_fully_const()) + log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); + + int offset = 0; + for (size_t i = 0; i < lhs.chunks.size(); i++) { + if (lhs.chunks[i].wire == NULL) + continue; + RTLIL::Wire *wire = lhs.chunks[i].wire; + RTLIL::SigSpec value = rhs.extract(offset, lhs.chunks[i].width); + if (value.width != wire->width) + log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs.chunks[i]), log_signal(value)); + log(" Setting init value: %s = %s\n", log_signal(wire), log_signal(value)); + wire->attributes["\\init"] = value.as_const(); + offset += wire->width; + } + } + } + + if (found_init) { + std::vector new_syncs; + for (auto &sync : proc->syncs) + if (sync->type == RTLIL::SyncType::STi) + delete sync; + else + new_syncs.push_back(sync); + proc->syncs.swap(new_syncs); + } +} + +struct ProcInitPass : public Pass { + ProcInitPass() : Pass("proc_init", "convert initial block to init attributes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_init [selection]\n"); + log("\n"); + log("This pass extracts the 'init' actions from processes (generated from verilog\n"); + log("'initial' blocks) and sets the initial value to the 'init' attribute on the\n"); + log("respective wire.\n"); + log("\n"); + } + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header("Executing PROC_INIT pass (extract init attributes).\n"); + + extra_args(args, 1, design); + + for (auto &mod_it : design->modules) + if (design->selected(mod_it.second)) + for (auto &proc_it : mod_it.second->processes) + if (design->selected(mod_it.second, proc_it.second)) + proc_init(mod_it.second, proc_it.second); + } +} ProcInitPass; +